diff --git a/.dockerignore b/.dockerignore index 707fbd5f..47d9cbdf 100644 --- a/.dockerignore +++ b/.dockerignore @@ -7,3 +7,5 @@ docs .git !.git/HEAD !.git/refs/* +config/ +!config/defaults-*.json diff --git a/.github/workflows/publish-image.yml b/.github/workflows/publish-image.yml index 387f55b0..352698f6 100644 --- a/.github/workflows/publish-image.yml +++ b/.github/workflows/publish-image.yml @@ -25,6 +25,7 @@ jobs: echo ::set-output name=channel::"latest" echo ::set-output name=version::main_$(cat VERSION)-${GITHUB_SHA::6} elif [[ "$GITHUB_REF" == "refs/heads/"* ]]; then + echo ::set-output name=channel::${GITHUB_REF/refs\/heads\//}-"latest" echo ::set-output name=version::${GITHUB_REF/refs\/heads\//}-$(cat VERSION)-${GITHUB_SHA::6} elif [[ "$GITHUB_REF" == "refs/tags/"* ]]; then echo ::set-output name=channel::${GITHUB_REF/refs\/tags\//} @@ -60,3 +61,5 @@ jobs: token: ${{ secrets.TELEGRAM_TOKEN }} message: "New Docker image has been built: ${{ steps.meta.outputs.tags }}" disable_web_page_preview: true + disable_notification: true + diff --git a/.gitignore b/.gitignore index b9367220..81b81617 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ /access/** /custom/** /ddos/** +/core/tools/hash.php !/access/.gitkeep !/custom/.gitkeep !/ddos/.gitkeep diff --git a/.htaccess b/.htaccess index 9db9cb98..aa828807 100644 --- a/.htaccess +++ b/.htaccess @@ -1,11 +1,23 @@ -# Block access to all dot files -RedirectMatch 404 /\..*$ +# Deny everything by default +Order deny,allow +Deny from all -# Block access to .git files and folders -RedirectMatch 404 /\.git +# Allow plain requests that get routed to index.php + + Allow from all + -# Block access to .json and .sql files -RedirectMatch 404 /*\.(json|sql)$ +# Allow endpoints we expect to be called directly + + Allow from all + -# Block access to specifc folders -RedirectMatch 404 /(access|config|custom|ddos|lang|sql|screens)/ +# Allow webhook setup, it's just static + + Allow from all + + +# Allow metrics without appending / + + Allow from all + diff --git a/VERSION b/VERSION index b8626c4c..62f94575 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4 +6 \ No newline at end of file diff --git a/commands/addgym.php b/commands/addgym.php deleted file mode 100644 index 460b8cd6..00000000 --- a/commands/addgym.php +++ /dev/null @@ -1,161 +0,0 @@ -' . getTranslation('invalid_input') . '' . CR . CR; - $msg .= getTranslation('gym_coordinates_format_error') . CR; - $msg .= getTranslation('gym_coordinates_format_example'); - send_message($update['message']['chat']['id'], $msg); - exit(); -} - -// Set gym name. -$gym_name = '#' . $update['message']['from']['id']; - -// Get address. -$addr = get_address($lat, $lon); -$address = format_address($addr); - -// Insert / update gym. -try { - - global $dbh; - - // Build query to check if gym is already in database or not - $rs = my_query(" - SELECT COUNT(*) AS count - FROM gyms - WHERE gym_name = '{$gym_name}' - "); - - $row = $rs->fetch(); - - // Gym already in database or new - if (empty($row['count'])) { - // insert gym in table. - debug_log('Gym not found in database gym list! Inserting gym "' . $gym_name . '" now.'); - $query = ' - INSERT INTO gyms (gym_name, lat, lon, address, show_gym) - VALUES (:gym_name, :lat, :lon, :address, 0) - '; - $msg = getTranslation('gym_added'); - } else { - // Get gym by temporary name. - $gym = get_gym_by_telegram_id($gym_name); - - // If gym is already in the database, make sure no raid is active before continuing! - if($gym) { - debug_log('Gym found in the database! Checking for active raid now!'); - $gym_id = $gym['id']; - - // Check for duplicate raid - $duplicate_id = 0; - $duplicate_id = active_raid_duplication_check($gym_id); - - // Continue with raid creation - if($duplicate_id > 0) { - debug_log('Active raid is in progress!'); - debug_log('Tell user to update the gymname and exit!'); - - // Show message that a raid is active on that gym. - $raid_id = $duplicate_id; - $raid = get_raid($raid_id); - - // Build message. - $msg = EMOJI_WARN . SP . getTranslation('raid_already_exists') . SP . EMOJI_WARN . CR . show_raid_poll_small($raid); - - // Tell user to update the gymname first to create another raid by location - $msg .= getTranslation('gymname_then_location'); - $keys = []; - - // Send message. - send_message($update['message']['chat']['id'], $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); - - exit(); - } else { - debug_log('No active raid found! Continuing now ...'); - } - } else { - // Set gym_id to 0 - $gym_id = 0; - debug_log('No gym found in the database! Continuing now ...'); - } - - // Update gyms table to reflect gym changes. - debug_log('Gym found in database gym list! Updating gym "' . $gym_name . '" now.'); - $query = ' - UPDATE gyms - SET lat = :lat, - lon = :lon, - address = :address - WHERE gym_name = :gym_name - '; - $msg = getTranslation('gym_updated'); - } - - $statement = $dbh->prepare($query); - $statement->execute([ - 'gym_name' => $gym_name, - 'lat' => $lat, - 'lon' => $lon, - 'address' => $address - ]); - - // Get last insert id. - if (empty($row['count'])) { - $gym_id = $dbh->lastInsertId(); - } - - // Gym details. - if($gym_id > 0) { - $gym = get_gym($gym_id); - $msg .= CR . CR . get_gym_details($gym); - $msg .= CR . getTranslation('gym_instructions'); - $msg .= CR . getTranslation('help_gym-edit'); - $msg .= CR . getTranslation('help_gym-name'); - $msg .= CR . getTranslation('help_gym-address'); - $msg .= CR . getTranslation('help_gym-note'); - $msg .= CR . getTranslation('help_gym-delete'); - } -} catch (PDOException $exception) { - - error_log($exception->getMessage()); - $dbh = null; - exit(); -} - -// Set keys. -$keys = []; - -// Send the message. -send_message($update['message']['chat']['id'], $msg, $keys, ['disable_web_page_preview' => 'true']); - -?> diff --git a/commands/delete.php b/commands/delete.php index df04936a..06083353 100644 --- a/commands/delete.php +++ b/commands/delete.php @@ -6,93 +6,64 @@ //debug_log($update); //debug_log($data); -// Check access. -bot_access_check($update, 'access-bot'); +if($botUser->accessCheck('delete-own', true)) { + $userRestriction = 'AND raids.user_id = ?'; + $binds = [$update['message']['chat']['id']]; +}elseif($botUser->accessCheck('delete-all', true)) { + $userRestriction = ''; + $binds = []; +}else { + $botUser->denyAccess(); +} + +$query = my_query(' + SELECT raids.*, gyms.gym_name + FROM raids + LEFT JOIN gyms + ON raids.gym_id = gyms.id + WHERE raids.end_time > UTC_TIMESTAMP + ' . $userRestriction . ' + ORDER BY raids.end_time ASC + LIMIT 20 +', $binds); -// Count results. -$count = 0; -$own_sql = ""; -$own_arr = []; -if(!bot_access_check($update, 'delete', true) && bot_access_check($update,'delete-own',true)) { - $own_sql = "AND users.user_id = :user_id"; - $own_arr = [":user_id"=>$update['message']['from']['id']]; +if($query->rowCount() == 0) { + $msg = '' . getTranslation('no_active_raids_found') . ''; + send_message(create_chat_object([$update['message']['chat']['id']]), $msg); + exit; } + // Init text and keys. $text = ''; $keys = []; -try { +while ($row = $query->fetch()) { + // Set text and keys. + $text .= $row['gym_name'] . CR; + $now = utcnow(); + $today = dt2date($now); + $raid_day = dt2date($row['start_time']); + $start = dt2time($row['start_time']); + $end = dt2time($row['end_time']); + $text .= get_local_pokemon_name($row['pokemon'], $row['pokemon_form']) . SP . '—' . SP . (($raid_day == $today) ? '' : ($raid_day . ', ')) . $start . SP . getTranslation('to') . SP . $end . CR . CR; + $keys[] = button($row['gym_name'], ['raids_delete', 'r' => $row['id']]); - $query = ' - SELECT - raids.*, gyms.lat , - gyms.lon , - gyms.address , - gyms.gym_name , - gyms.ex_gym , - users. NAME - FROM - raids - LEFT JOIN gyms ON raids.gym_id = gyms.id - LEFT JOIN users ON raids.user_id = users.user_id - WHERE - raids.end_time > UTC_TIMESTAMP() - '.$own_sql.' - ORDER BY - raids.end_time ASC - LIMIT 20 - '; - $statement = $dbh->prepare( $query ); - $statement->execute($own_arr); - while ($row = $statement->fetch()) { - // Set text and keys. - $text .= $row['gym_name'] . CR; - $now = utcnow(); - $today = dt2date($now); - $raid_day = dt2date($row['start_time']); - $start = dt2time($row['start_time']); - $end = dt2time($row['end_time']); - $text .= get_local_pokemon_name($row['pokemon'], $row['pokemon_form']) . SP . '—' . SP . (($raid_day == $today) ? '' : ($raid_day . ', ')) . $start . SP . getTranslation('to') . SP . $end . CR . CR; - $keys[] = array( - 'text' => $row['gym_name'], - 'callback_data' => $row['id'] . ':raids_delete:0' - ); - - // Counter++ - $count = $count + 1; - } } -catch (PDOException $exception) { - error_log($exception->getMessage()); - $dbh = null; - exit; -} +// Get the inline key array. +$keys = inline_key_array($keys, 1); -// Set message. -if($count == 0) { - $msg = '' . getTranslation('no_active_raids_found') . ''; -} else { - // Get the inline key array. - $keys = inline_key_array($keys, 1); +// Add exit key. +$keys[][] = button(getTranslation('abort'), 'exit'); - // Add exit key. - $keys[] = [ - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ]; - // Build message. - $msg = '' . getTranslation('list_all_active_raids') . ':' . CR; - $msg .= $text; - $msg .= '' . getTranslation('select_gym_name') . '' . CR; -} +// Build message. +$msg = '' . getTranslation('list_all_active_raids') . ':' . CR; +$msg .= $text; +$msg .= '' . getTranslation('select_gym_name') . '' . CR; // Build callback message string. $callback_response = 'OK'; // Send message. -send_message($update['message']['chat']['id'], $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); -?> +send_message(create_chat_object([$update['message']['chat']['id']]), $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); diff --git a/commands/events.php b/commands/events.php new file mode 100644 index 00000000..458a30a5 --- /dev/null +++ b/commands/events.php @@ -0,0 +1,28 @@ +accessCheck('event-manage'); + +$q = my_query('SELECT * FROM events'); + +$msg = '' . getTranslation('events_manage') . '' . CR; + +foreach($q->fetchAll() as $event) { + if($event['id'] == EVENT_ID_EX) $event['name'] = getTranslation('Xstars'); + if(empty($event['description'])) $event['description'] = '' . getTranslation('events_no_description') . ''; + $msg .= '' . $event['name'] . '' . CR; + $msg .= $event['description'] . CR . CR; +} + +$keys[][] = button(getTranslation('events_manage'), 'events'); +$keys[][] = button(getTranslation('events_create'), 'events_add'); +$keys[][] = button(getTranslation('done'), ['exit', 'd' => '1']); + +// Send message. +send_message(create_chat_object([$update['message']['chat']['id']]), $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); diff --git a/commands/exreport.php b/commands/exreport.php index 7d793ed7..6d818307 100644 --- a/commands/exreport.php +++ b/commands/exreport.php @@ -7,7 +7,7 @@ //debug_log($data); // Check access. -bot_access_check($update, 'ex-report'); +$botUser->accessCheck('ex-report'); // Init empty keys array. $keys = []; @@ -21,72 +21,61 @@ $keys[3]['callback_data'] = 0; $i = 4; -try { +$query = my_query(' + SELECT + g.gym_name , + SUM( + CASE + WHEN a.cancel = FALSE + OR a.raid_done = FALSE THEN + ( + a.extra_in_person + a.extra_alien + 1 + ) + ELSE + 0 + END + ) AS Total_attended , + count(DISTINCT r.id) AS Total_raids , + ROUND( + ( + SUM( + CASE + WHEN a.cancel = FALSE + OR a.raid_done = FALSE THEN + ( + a.extra_in_person + a.extra_alien + 1 + ) + ELSE + 0 + END + ) / count(DISTINCT r.id) * 2 + ) + 3 + ) AS players_needed_to_trigger + FROM + raids r + LEFT JOIN attendance a ON a.raid_id = r.id + LEFT JOIN gyms g ON g.id = r.gym_id + WHERE + g.ex_gym = 1 + AND WEEK(r.start_time) BETWEEN WEEK(now()) - 2 + AND WEEK(now()) + GROUP BY + g.gym_name +'); +while ($row = $query->fetch()) { - $query = ' - SELECT - g.gym_name , - SUM( - CASE - WHEN a.cancel = FALSE - OR a.raid_done = FALSE THEN - ( - a.extra_in_person + a.extra_alien + 1 - ) - ELSE - 0 - END - ) AS Total_attended , - count(DISTINCT r.id) AS Total_raids , - ROUND( - ( - SUM( - CASE - WHEN a.cancel = FALSE - OR a.raid_done = FALSE THEN - ( - a.extra_in_person + a.extra_alien + 1 - ) - ELSE - 0 - END - ) / count(DISTINCT r.id) * 2 - ) + 3 - ) AS players_needed_to_trigger - FROM - raids r - LEFT JOIN attendance a ON a.raid_id = r.id - LEFT JOIN gyms g ON g.id = r.gym_id - WHERE - g.ex_gym = 1 - AND WEEK(r.start_time) BETWEEN WEEK(now()) - 2 - AND WEEK(now()) - GROUP BY - g.gym_name - '; - $statement = $dbh->prepare( $query ); - $statement->execute(); - while ($row = $statement->fetch()) { - - $keys[$i]['text'] = $row['gym_name']; - $keys[$i]['callback_data'] = 0; - - $keys[$i+1]['text'] = $row['Total_attended']; - $keys[$i+1]['callback_data'] = 0; - - $keys[$i+2]['text'] = $row['Total_raids']; - $keys[$i+2]['callback_data'] = 0; - - $keys[$i+3]['text'] = $row['players_needed_to_trigger']; - $keys[$i+3]['callback_data'] = 0; - $i = $i+4; - } -} -catch (PDOException $exception) { + $keys[$i]['text'] = $row['gym_name']; + $keys[$i]['callback_data'] = 0; + + $keys[$i+1]['text'] = $row['Total_attended']; + $keys[$i+1]['callback_data'] = 0; + + $keys[$i+2]['text'] = $row['Total_raids']; + $keys[$i+2]['callback_data'] = 0; - error_log($exception->getMessage()); - $dbh = null; - exit; + $keys[$i+3]['text'] = $row['players_needed_to_trigger']; + $keys[$i+3]['callback_data'] = 0; + $i = $i+4; } // Get the inline key array. @@ -96,5 +85,4 @@ $msg = 'EX Raid Report'; // Send message. -send_message($update['message']['chat']['id'], $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); -?> +send_message(create_chat_object([$update['message']['chat']['id']]), $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); diff --git a/commands/friendsearch.php b/commands/friendsearch.php index fe740994..77afa818 100644 --- a/commands/friendsearch.php +++ b/commands/friendsearch.php @@ -6,7 +6,7 @@ //debug_log($update); //debug_log($data); -bot_access_check($update, 'friendsearch'); +$botUser->accessCheck('friendsearch'); // Trim away everything before "/FRIENDSEARCH" $searchterm = $update['message']['text']; @@ -14,16 +14,13 @@ debug_log($searchterm, 'SEARCHTERM'); -$query = "SELECT user_id, name, team, level, trainername FROM users WHERE trainername LIKE :tn"; -$statement = $dbh->prepare( $query ); -$statement->execute([':tn' => $searchterm]); -if($statement->rowCount() == 1) { - $result = $statement->fetch(); - $msg = ($result['team'] === NULL) ? ($GLOBALS['teams']['unknown'] . ' ') : ($GLOBALS['teams'][$result['team']] . ' '); - $msg .= ($result['level'] == 0) ? ('00 ') : (($result['level'] < 10) ? ('0' . $result['level'] . ' ') : ('' . $result['level'] . ' ')); - $msg .= "".$result['name']." - ".$result['trainername'].""; +$query = my_query('SELECT user_id, name, team, level, trainername FROM users WHERE trainername LIKE :tn', [':tn' => $searchterm]); +if($query->rowCount() == 1) { + $result = $query->fetch(); + $msg = ($result['team'] === NULL) ? ($GLOBALS['teams']['unknown'] . ' ') : ($GLOBALS['teams'][$result['team']] . ' '); + $msg .= ($result['level'] == 0) ? ('00 ') : (($result['level'] < 10) ? ('0' . $result['level'] . ' ') : ('' . $result['level'] . ' ')); + $msg .= '' . $result['name'] . ' - ' . $result['trainername'] . ''; }else { - $msg = $searchterm.CR. getTranslation('trainer_not_found'); + $msg = $searchterm.CR. getTranslation('trainer_not_found'); } -send_message($update['message']['chat']['id'], $msg, [], ['reply_markup' => ['selective' => true]]); -?> \ No newline at end of file +send_message(create_chat_object([$update['message']['chat']['id']]), $msg, [], ['reply_markup' => ['selective' => true]]); diff --git a/commands/get.php b/commands/get.php new file mode 100644 index 00000000..df31fbb1 --- /dev/null +++ b/commands/get.php @@ -0,0 +1,72 @@ +accessCheck('config-get'); + +// Get all allowed configs. +$allowed = explode(',', $config->ALLOWED_TELEGRAM_CONFIG); +$msg = '' . getTranslation('config') . ':' . CR . CR; + +// Get config restrictions for boolean input +$allowed_bool = explode(',', $config->ALLOW_ONLY_TRUE_FALSE); + +// Get config restrictions for numeric input +$allowed_numbers = explode(',', $config->ALLOW_ONLY_NUMBERS); + +// Get config. +$cfile = botSpecificConfigFile('config.json'); +if(is_file($cfile)) { + $str = file_get_contents($cfile); + $json = json_decode($str, true); +} + +// Get config aliases. +$afile = botSpecificConfigFile('alias.json'); +if(is_file($afile)) { + $astr = file_get_contents($afile); + $ajson = json_decode($astr, true); +} + +// Write to log. +debug_log('User requested the allowed telegram configs'); +debug_log('Allowed telegram configs: ' . $config->ALLOWED_TELEGRAM_CONFIG); +debug_log('Allow only boolean input: ' . $config->ALLOW_ONLY_TRUE_FALSE); +debug_log('Allow only numeric input: ' . $config->ALLOW_ONLY_NUMBERS); + +// Any configs allowed? +if(empty($allowed)) { + send_message(create_chat_object([$update['message']['chat']['id']]), getTranslation('not_supported')); + exit; +} +foreach($json as $cfg_name => $cfg_value) { + // Only allowed configs + if(in_array($cfg_name, $allowed)) { + // Is alias set? + $alias = ''; + if(isset($ajson[$cfg_name])){ + $alias = $ajson[$cfg_name]; + } + if($cfg_value === true) $cfg_value = 'true'; + elseif($cfg_value === false) $cfg_value = 'false'; + // Config name / Alias + value + $msg .= (empty($alias) ? $cfg_name : $alias) . SP . (empty($cfg_value) ? '' . getTranslation('no_value') . '' : $cfg_value); + + // Only bool? + if(in_array($cfg_name, $allowed_bool)) { + $msg .= SP . '(' . getTranslation('help_only_bool') . ')'; + + // Only numbers? + } else if(in_array($cfg_name, $allowed_numbers)) { + $msg .= SP . '(' . getTranslation('help_only_numbers') . ')'; + + } + $msg .= CR; + } +} +send_message(create_chat_object([$update['message']['chat']['id']]), $msg); diff --git a/commands/gym.php b/commands/gym.php index 3dfad347..3da5d685 100644 --- a/commands/gym.php +++ b/commands/gym.php @@ -1,4 +1,5 @@ accessCheck('gym-details'); // Set keys. -$keys_and_gymarea = raid_edit_gyms_first_letter_keys('gym_details', false, false, 'gym_letter'); +$gymarea = resolveDefaultGymarea($botUser->userId); +$keys_and_gymarea = gymMenu('gym', false, 1, false, $gymarea); $keys = $keys_and_gymarea['keys']; // Set message. -$msg = '' . getTranslation('show_gym_details') . CR . CR; -if($config->ENABLE_GYM_AREAS) { - if($keys_and_gymarea['gymarea_name'] == '') { - $msg .= getTranslation('select_gym_area') . '' . CR; - }elseif($config->DEFAULT_GYM_AREA !== false) { - if($keys_and_gymarea['letters']) { - $msg .= getTranslation('select_gym_first_letter_or_gym_area') . '' . CR; - }else { - $msg .= getTranslation('select_gym_name_or_gym_area') . '' . CR; - } - }else { - if($keys_and_gymarea['letters']) { - $msg .= getTranslation('select_gym_first_letter') . '' . CR; - }else { - $msg .= getTranslation('select_gym_name') . '' . CR; - } - } -}else { - if($keys_and_gymarea['letters']) { - $msg .= getTranslation('select_gym_first_letter') . '' . CR; - }else { - $msg .= getTranslation('select_gym_name') . '' . CR; - } -} -$msg.= (($keys_and_gymarea['gymarea_name'] != '') ? CR . CR . getTranslation('current_gymarea') . ': ' . $keys_and_gymarea['gymarea_name'] : ''); - -// Add key for hidden gyms. -$h_keys = []; -if($config->ENABLE_GYM_AREAS == false or ($config->ENABLE_GYM_AREAS == true && $config->DEFAULT_GYM_AREA !== false)) { - // Add key for hidden gyms. - $h_keys[] = universal_inner_key($h_keys, '0', 'gym_hidden_letter', 'gym_details', getTranslation('hidden_gyms')); - $h_keys = inline_key_array($h_keys, 1); -} - -// Merge keys. -$keys = array_merge($h_keys, $keys); - -$keys[] = [ - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] -]; +$msg = '' . getTranslation('show_gym_details') . '' . CR . CR; +$msg.= $keys_and_gymarea['gymareaTitle']; // Send message. -send_message($update['message']['chat']['id'], $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true], 'disable_web_page_preview' => 'true']); - -?> +send_message(create_chat_object([$update['message']['chat']['id']]), $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true], 'disable_web_page_preview' => 'true']); diff --git a/commands/gymaddress.php b/commands/gymaddress.php deleted file mode 100644 index 9dc2a31d..00000000 --- a/commands/gymaddress.php +++ /dev/null @@ -1,95 +0,0 @@ -' . getTranslation('gym_id_address_missing') . ''; - $msg .= CR . CR . getTranslation('gym_address_instructions'); - $msg .= CR . getTranslation('gym_address_example'); - $msg .= CR . CR . getTranslation('gym_address_reset'); - $msg .= CR . getTranslation('gym_address_reset_example'); - $msg .= CR . CR . getTranslation('gym_get_id_details'); - - // Set keys. - $keys = []; -} else { - // Set keys. - $keys = []; - - // Get gym id. - $split_id_info = explode(',', $id_info,2); - $id = $split_id_info[0]; - $info = $split_id_info[1]; - $info = trim($info); - - // Make sure we have a valid gym id. - $gym = false; - if(is_numeric($id)) { - $gym = get_gym($id); - } - - // Update gym info. - if($gym && !empty($info) && strtolower($info) == 'reset') { - debug_log('Deleting address for gym with ID: ' . $id); - my_query( - " - UPDATE gyms - SET address = NULL - WHERE id = {$id} - " - ); - - // Set message. - $msg = get_gym_details($gym); - $msg .= CR . '' . getTranslation('gym_address_deleted') . ''; - } else if($gym && !empty($info)) { - debug_log('Adding address for gym with ID: ' . $id); - debug_log('Gym note: ' . $info); - $stmt = $dbh->prepare( - " - UPDATE gyms - SET address = :info - WHERE id = :id - " - ); - $stmt->execute(['info' => $info, 'id' => $id]); - - // Set message. - $msg = get_gym_details($gym); - $msg .= EMOJI_NEW . SP . $info; - $msg .= CR . CR . '' . getTranslation('gym_address_added') . ''; - } else if($gym && empty($info)) { - debug_log('Missing gym address!'); - // Set message. - $msg .= CR . '' . getTranslation('gym_id_address_missing') . ''; - $msg .= CR . CR . getTranslation('gym_address_instructions'); - $msg .= CR . getTranslation('gym_address_example'); - $msg .= CR . CR . getTranslation('gym_address_reset'); - $msg .= CR . getTranslation('gym_address_reset_example'); - $msg .= CR . CR . getTranslation('gym_get_id_details'); - } else { - // Set message. - $msg .= getTranslation('invalid_input'); - } -} - -// Send message. -send_message($update['message']['chat']['id'], $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true], 'disable_web_page_preview' => 'true']); - -?> diff --git a/commands/gymgps.php b/commands/gymgps.php deleted file mode 100644 index 7a76007f..00000000 --- a/commands/gymgps.php +++ /dev/null @@ -1,101 +0,0 @@ -' . getTranslation('gym_id_gps_missing') . ''; - $msg .= CR . CR . getTranslation('gym_gps_instructions'); - $msg .= CR . getTranslation('gym_gps_example'); - - // Set keys. - $keys = []; -} else { - // Set keys. - $keys = []; - - // Get gym id. - $split_id_info = explode(',', $id_info,2); - $id = $split_id_info[0]; - $info = $split_id_info[1]; - $info = trim($info); - - // Count commas given in info. - $count = substr_count($info, ","); - - // 1 comma as it should be? - // E.g. 52.5145434,13.3501189 - if($count == 1) { - $lat_lon = explode(',', $info); - $lat = $lat_lon[0]; - $lon = $lat_lon[1]; - - // Lat and lon with comma instead of dot? - // E.g. 52,5145434,13,3501189 - } else if($count == 3) { - $lat_lon = explode(',', $info); - $lat = $lat_lon[0] . '.' . $lat_lon[1]; - $lon = $lat_lon[2] . '.' . $lat_lon[3]; - } else { - // Invalid input - send the message and exit. - $msg = '' . getTranslation('invalid_input') . '' . CR . CR; - $msg .= getTranslation('gym_gps_coordinates_format_error') . CR; - $msg .= getTranslation('gym_gps_example'); - send_message($update['message']['chat']['id'], $msg); - exit(); - } - - // Make sure we have a valid gym id. - $gym = false; - if(is_numeric($id)) { - $gym = get_gym($id); - } - - if($gym && !empty($info)) { - debug_log('Updating gps coordinates for gym with ID: ' . $id); - debug_log('Gym latitude: ' . $lat); - debug_log('Gym longitude: ' . $lon); - my_query( - " - UPDATE gyms - SET lat = {$lat}, - lon = {$lon} - WHERE id = {$id} - " - ); - - // Set message. - $msg = get_gym_details($gym); - $msg .= EMOJI_NEW . SP . $info; - $msg .= CR . CR . '' . getTranslation('gym_gps_added') . ''; - } else if($gym && empty($info)) { - debug_log('Missing gym coordinates!'); - // Set message. - $msg .= CR . '' . getTranslation('gym_id_gps_missing') . ''; - $msg .= CR . CR . getTranslation('gym_gps_instructions'); - $msg .= CR . getTranslation('gym_gps_example'); - } else { - // Set message. - $msg .= getTranslation('invalid_input'); - } -} - -// Send message. -send_message($update['message']['chat']['id'], $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true], 'disable_web_page_preview' => 'true']); - -?> diff --git a/commands/gymname.php b/commands/gymname.php index d6f0a7e2..330f653e 100644 --- a/commands/gymname.php +++ b/commands/gymname.php @@ -1,98 +1,53 @@ accessCheck('gym-name'); // Get gym by name. // Trim away everything before "/gymname " -$id_info = $update['message']['text']; -$id_info = substr($id_info, 9); -$id_info = trim($id_info); - -// Display keys to get gym ids. -if(empty($id_info)) { - debug_log('Missing gym name!'); - // Set message. - $msg = '' . getTranslation('gym_id_name_missing') . ''; - $msg .= CR . CR . getTranslation('gym_name_instructions'); - $msg .= CR . getTranslation('gym_name_example'); - $msg .= CR . CR . getTranslation('gym_get_id_details'); - - // Set keys. - $keys = []; +$input = trim(substr($update['message']['text'], 9)); + +// Init vars. +$gym = false; +$id = 0; +$tg_id = '#' . $update['message']['from']['id']; + +// Maybe get gym by telegram id? +$gym = get_gym_by_telegram_id($tg_id); + +// Update gym info. +if($gym && !empty($input) && $gym['id'] > 0) { + debug_log('Changing name for gym with ID: ' . $gym['id']); + debug_log('Gym name: ' . $input); + my_query(' + UPDATE gyms + SET gym_name = :info + WHERE id = :id + ', ['info' => $input, 'id' => $gym['id']] + ); + $gym['gym_name'] = $input; + // Set message. + $msg = get_gym_details($gym); + $msg .= CR . '' . getTranslation('gym_name_updated') . ''; +} else if($gym && empty($info)) { + debug_log('Missing gym name!'); + // Set message. + $msg = CR . '' . getTranslation('gym_id_name_missing') . ''; + $msg .= CR . CR . getTranslation('gym_name_instructions'); + $msg .= CR . getTranslation('gym_name_example'); } else { - // Set keys. - $keys = []; - - // Init vars. - $gym = false; - $info = ''; - $id = 0; - $tg_id = '#' . $update['message']['from']['id']; - - // Get gym id. - if(substr_count($id_info, ',') >= 1) { - $split_id_info = explode(',', $id_info,2); - $id = $split_id_info[0]; - $info = $split_id_info[1]; - $info = trim($info); - - // Make sure we have a valid gym id. - if(is_numeric($id)) { - $gym = get_gym($id); - } - } - - // Maybe get gym by telegram id? - if(!$gym) { - $gym = get_gym_by_telegram_id($tg_id); - // Get new id. - if($gym) { - $id = $gym['id']; - $info = $id_info; - } - } - - // Update gym info. - if($gym && !empty($info) && $id > 0) { - debug_log('Changing name for gym with ID: ' . $id); - debug_log('Gym name: ' . $info); - $stmt = $dbh->prepare( - " - UPDATE gyms - SET gym_name = :info - WHERE id = :id - " - ); - $stmt->execute([ - 'info' => $info, - 'id' => $id - ]); - - // Set message. - $gym = get_gym($id); - $msg = get_gym_details($gym); - $msg .= CR . '' . getTranslation('gym_name_updated') . ''; - } else if($gym && empty($info)) { - debug_log('Missing gym name!'); - // Set message. - $msg .= CR . '' . getTranslation('gym_id_name_missing') . ''; - $msg .= CR . CR . getTranslation('gym_name_instructions'); - $msg .= CR . getTranslation('gym_name_example'); - $msg .= CR . CR . getTranslation('gym_get_id_details'); - } else { - // Set message. - $msg .= getTranslation('invalid_input'); - } + // Set message. + $msg = getTranslation('gym_not_found'); } // Send message. -send_message($update['message']['chat']['id'], $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true], 'disable_web_page_preview' => 'true']); - -?> +send_message(create_chat_object([$update['message']['chat']['id']]), $msg, [], ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true], 'disable_web_page_preview' => 'true']); diff --git a/commands/gymnote.php b/commands/gymnote.php deleted file mode 100644 index f5652dd0..00000000 --- a/commands/gymnote.php +++ /dev/null @@ -1,98 +0,0 @@ -' . getTranslation('gym_id_note_missing') . ''; - $msg .= CR . CR . getTranslation('gym_note_instructions'); - $msg .= CR . getTranslation('gym_note_example'); - $msg .= CR . CR . getTranslation('gym_note_reset'); - $msg .= CR . getTranslation('gym_note_reset_example'); - $msg .= CR . CR . getTranslation('gym_get_id_details'); - - // Set keys. - $keys = []; -} else { - // Set keys. - $keys = []; - - // Get gym id. - $split_id_info = explode(',', $id_info,2); - $id = $split_id_info[0]; - $info = $split_id_info[1]; - $info = trim($info); - - // Make sure we have a valid gym id. - $gym = false; - if(is_numeric($id)) { - $gym = get_gym($id); - } - - // Update gym info. - if($gym && !empty($info) && strtolower($info) == 'reset') { - debug_log('Deleting gym note for gym with ID: ' . $id); - my_query( - " - UPDATE gyms - SET gym_note = NULL - WHERE id = {$id} - " - ); - - // Set message. - $msg = get_gym_details($gym); - $msg .= CR . '' . getTranslation('gym_note_deleted') . ''; - } else if($gym && !empty($info)) { - debug_log('Adding gym note for gym with ID: ' . $id); - debug_log('Gym note: ' . $info); - $stmt = $dbh->prepare( - " - UPDATE gyms - SET gym_note = :info - WHERE id = :id - " - ); - $stmt->execute([ - 'info' => $info, - 'id' => $id - ]); - - // Set message. - $msg = get_gym_details($gym); - $msg .= CR . CR . '' . getTranslation('gym_note_new') . '' . CR . EMOJI_INFO . SP . $info; - $msg .= CR . CR . '' . getTranslation('gym_note_added') . ''; - } else if($gym && empty($info)) { - debug_log('Missing gym note!'); - // Set message. - $msg .= CR . '' . getTranslation('gym_id_note_missing') . ''; - $msg .= CR . CR . getTranslation('gym_note_instructions'); - $msg .= CR . getTranslation('gym_note_example'); - $msg .= CR . CR . getTranslation('gym_note_reset'); - $msg .= CR . getTranslation('gym_note_reset_example'); - $msg .= CR . CR . getTranslation('gym_get_id_details'); - } else { - // Set message. - $msg .= getTranslation('invalid_input'); - } -} - -// Send message. -send_message($update['message']['chat']['id'], $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true], 'disable_web_page_preview' => 'true']); - -?> diff --git a/commands/help.php b/commands/help.php index 66f08396..ddc1478c 100644 --- a/commands/help.php +++ b/commands/help.php @@ -1,84 +1,70 @@ accessCheck('help', true); // Display help for each permission -if($access && (is_file(ROOT_PATH . '/access/' . $access) || $access == 'BOT_ADMINS')) { - // Get permissions from file. +if($access) { + if($botUser->userPrivileges['grantedBy'] == 'BOT_ADMINS') { + $permissions = array(); + $permissions[] = 'access-bot'; + $permissions[] = 'create'; + $permissions[] = 'ex-raids'; + $permissions[] = 'raid-duration'; + $permissions[] = 'list'; + $permissions[] = 'listall'; + $permissions[] = 'overview'; + $permissions[] = 'delete-all'; + $permissions[] = 'pokemon-all'; + $permissions[] = 'trainer'; + $permissions[] = 'gym-details'; + $permissions[] = 'gym-edit'; + $permissions[] = 'gym-add'; + $permissions[] = 'portal-import'; + $permissions[] = 'config-get'; + $permissions[] = 'config-set'; + $permissions[] = 'pokedex'; + $permissions[] = 'history'; + $permissions[] = 'event-manage'; + $permissions[] = 'help'; + } else { + // Get permissions. + $permissions = $botUser->userPrivileges['privileges']; + } - if($access == 'BOT_ADMINS') { - $permissions = array(); - $permissions[] = 'access-bot'; - $permissions[] = 'create'; - $permissions[] = 'ex-raids'; - $permissions[] = 'raid-duration'; - $permissions[] = 'list'; - $permissions[] = 'listall'; - $permissions[] = 'overview'; - $permissions[] = 'delete-all'; - $permissions[] = 'pokemon-all'; - $permissions[] = 'trainer'; - $permissions[] = 'gym-details'; - $permissions[] = 'gym-edit'; - $permissions[] = 'gym-name'; - $permissions[] = 'gym-address'; - $permissions[] = 'gym-gps'; - $permissions[] = 'gym-note'; - $permissions[] = 'gym-add'; - $permissions[] = 'portal-import'; - $permissions[] = 'config-get'; - $permissions[] = 'config-set'; - $permissions[] = 'pokedex'; - $permissions[] = 'help'; - } else { - // Get permissions from file. - $permissions = file(ROOT_PATH . '/access/' . $access, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); - } - - // Write to log. - debug_log($permissions,'PERMISSIONS: '); + // Write to log. + debug_log($permissions,'PERMISSIONS: '); - // Show help header. - debug_log('Showing help to user now'); - $msg = '' . getTranslation('personal_help') . '' . CR . CR; + // Show help header. + debug_log('Showing help to user now'); + $msg = '' . getTranslation('personal_help') . '' . CR . CR; - // Raid via location? - if($config->RAID_VIA_LOCATION) { - $msg .= EMOJI_CLIPPY . SP . getTranslation('help_create_via_location') . CR . CR; - } + // Raid via location? + if($config->RAID_VIA_LOCATION) { + $msg .= EMOJI_CLIPPY . SP . getTranslation('help_create_via_location') . CR . CR; + } - // Show help. - foreach($permissions as $id => $p) { - if($p == 'access-bot' || strpos($p, 'share-') === 0 || strpos($p, 'ignore-') === 0) continue; - if(getTranslation('help_' . $p)) { - $msg .= getTranslation('help_' . $p) . CR . CR; - } + // Show help. + foreach($permissions as $id => $p) { + if($p == 'access-bot' || strpos($p, 'share-') === 0 || strpos($p, 'ignore-') === 0) continue; + if(getTranslation('help_' . $p)) { + $msg .= getTranslation('help_' . $p) . CR . CR; } + } } elseif($config->TUTORIAL_MODE) { - if(new_user($update['message']['from']['id'])) { - $msg = $tutorial[0]['msg_new']; - }else { - $msg = $tutorial[0]['msg']; - } - $keys = [ - [ - [ - 'text' => getTranslation('next'), - 'callback_data' => '0:tutorial:0' - ] - ] - ]; - $photo = $tutorial[0]['photo']; - send_photo($update['message']['from']['id'],$photo, $msg, $keys, ['disable_web_page_preview' => 'true']); - exit(); + if(new_user($update['message']['from']['id'])) { + $msg = $tutorial[0]['msg_new']; + }else { + $msg = $tutorial[0]['msg']; + } + $keys[][] = button(getTranslation('next'), 'tutorial'); + $photo = $tutorial[0]['photo']; + send_photo(create_chat_object([$update['message']['from']['id']]),$photo, false, $msg, $keys, ['disable_web_page_preview' => 'true']); + exit(); // No help for the user. } else { - $msg = getTranslation('bot_access_denied'); + $msg = getTranslation('bot_access_denied'); } // Send message. -send_message($update['message']['from']['id'], $msg); - -?> - +send_message(create_chat_object([$update['message']['chat']['id']]), $msg); diff --git a/commands/history.php b/commands/history.php index edd09dc3..ef41ea1c 100644 --- a/commands/history.php +++ b/commands/history.php @@ -7,19 +7,17 @@ //debug_log($data); // Check access. -bot_access_check($update, 'history'); +$botUser->accessCheck('history'); require_once(LOGIC_PATH .'/history.php'); $msg_keys = create_history_date_msg_keys(); if($msg_keys === false) { - $msg = getTranslation('history_no_raids_found'); - $keys = []; + $msg = getTranslation('history_no_raids_found'); + $keys = []; }else { - $msg = $msg_keys[0]; - $keys = $msg_keys[1]; + $msg = $msg_keys[0]; + $keys = $msg_keys[1]; } -send_message($update['message']['chat']['id'], $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true], 'disable_web_page_preview' => 'true']); - -?> +send_message(create_chat_object([$update['message']['chat']['id']]), $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true], 'disable_web_page_preview' => 'true']); diff --git a/commands/list.php b/commands/list.php index 59545550..9474d199 100644 --- a/commands/list.php +++ b/commands/list.php @@ -8,31 +8,34 @@ //debug_log($data); // Check access. -bot_access_check($update, 'list'); - -// Init text and keys. -$text = ''; -$keys = []; - -$event_permissions = bot_access_check($update, 'event',true); - +$botUser->accessCheck('list'); + +$event_sql = 'event IS NULL'; +if($botUser->accessCheck('ex-raids', true)) { + if($botUser->accessCheck('event-raids', true)) + $event_sql = ''; + else + $event_sql .= ' OR event = ' . EVENT_ID_EX; +}elseif($botUser->accessCheck('event-raids', true)) { + $event_sql = 'event != ' . EVENT_ID_EX .' OR event IS NULL'; +} +$event_sql = ($event_sql == '') ? '' : 'AND ('.$event_sql.')'; // Get last 12 active raids data. -$rs = my_query( - ' - SELECT raids.pokemon, raids.pokemon_form, raids.id, raids.user_id, raids.spawn, raids.start_time, raids.end_time, raids.gym_team, raids.gym_id, raids.level, raids.move1, raids.move2, raids.gender, raids.event, raids.event_note, - gyms.lat, gyms.lon, gyms.address, gyms.gym_name, gyms.ex_gym, gyms.gym_note, - start_time, end_time, - TIME_FORMAT(TIMEDIFF(end_time, UTC_TIMESTAMP()) + INTERVAL 1 MINUTE, \'%k:%i\') AS t_left, - (SELECT COUNT(*) FROM raids WHERE end_time>UTC_TIMESTAMP()) AS r_active +$rs = my_query(' + SELECT raids.pokemon, raids.pokemon_form, raids.id, raids.spawn, raids.start_time, raids.end_time, raids.level, raids.event, + gyms.gym_name, gyms.ex_gym, + events.name as event_name, + (SELECT COUNT(*) FROM raids WHERE end_time>UTC_TIMESTAMP() ' . $event_sql . ') as r_active FROM raids LEFT JOIN gyms ON raids.gym_id = gyms.id + LEFT JOIN events + ON events.id = raids.event WHERE end_time>UTC_TIMESTAMP() - ' . ($event_permissions ? '' : 'AND event IS NULL' ) . ' + ' . $event_sql . ' ORDER BY end_time ASC LIMIT 12 - ' -); +'); // Get the raids. $raids = $rs->fetchAll(); @@ -40,72 +43,59 @@ debug_log($raids); // Did we get any raids? -if(isset($raids[0]['r_active'])) { - debug_log($raids[0]['r_active'], 'Active raids:'); - - // More raids as we like? - if($raids[0]['r_active'] > 12) { - // Forward to /listall - debug_log('Too much raids, forwarding to /listall'); - $skip_access = true; - include_once(ROOT_PATH . '/commands/listall.php'); - exit(); - - // Just enough raids to display at once - } else { - //while ($raid = $rs->fetch()) { - foreach($raids as $raid) { - // Set text and keys. - $gym_name = $raid['gym_name']; - if(empty($gym_name)) { - $gym_name = ''; - } - $resolved_boss = resolve_raid_boss($raid['pokemon'], $raid['pokemon_form'], $raid['spawn'], $raid['level']); - - $text .= $gym_name . CR; - $raid_day = dt2date($raid['start_time']); - $now = utcnow(); - $today = dt2date($now); - $start = dt2time($raid['start_time']); - $end = dt2time($raid['end_time']); - $text .= get_local_pokemon_name($resolved_boss['pokedex_id'], $resolved_boss['pokemon_form_id']) . SP . '-' . SP . (($raid_day == $today) ? '' : ($raid_day . ', ')) . $start . SP . getTranslation('to') . SP . $end . CR . CR; - - // Pokemon is an egg? - $eggs = $GLOBALS['eggs']; - if(in_array($resolved_boss['pokedex_id'], $eggs)) { - $keys_text = EMOJI_EGG . SP . $gym_name; - } else { - $keys_text = $gym_name; - } - - $keys[] = array( - 'text' => $keys_text, - 'callback_data' => $raid['id'] . ':raids_list:0' - ); - } +if(count($raids) == 0) { + $msg = '' . getTranslation('no_active_raids_found') . ''; + send_message(create_chat_object([$update['message']['chat']['id']]), $msg, [], ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); + exit(); +} - // Get the inline key array. - $keys = inline_key_array($keys, 1); +debug_log($raids[0]['r_active'], 'Active raids:'); +// More raids as we like? +if($raids[0]['r_active'] > 12 && $botUser->accessCheck('listall', true)) { + // Forward to /listall + debug_log('Too much raids, forwarding to /listall'); + $skip_access = true; + include_once(ROOT_PATH . '/commands/listall.php'); + exit(); +} - // Add exit key. - $keys[] = [ - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ]; +// Just enough raids to display at once +$text = ''; +foreach($raids as $raid) { + // Set text and keys. + $gym_name = $raid['gym_name']; + if(empty($gym_name)) { + $gym_name = ''; + } + $resolved_boss = resolve_raid_boss($raid['pokemon'], $raid['pokemon_form'], $raid['spawn'], $raid['level']); + + $text .= ($raid['ex_gym'] === 1 ? EMOJI_STAR . SP : '') . $gym_name . CR; + $raid_day = dt2date($raid['start_time']); + $now = utcnow(); + $today = dt2date($now); + $start = dt2time($raid['start_time']); + $end = dt2time($raid['end_time']); + $text .= (!empty($raid['event_name']) ? $raid['event_name'] . CR : '' ); + $text .= get_local_pokemon_name($resolved_boss['pokedex_id'], $resolved_boss['pokemon_form_id']) . SP . '-' . SP . (($raid_day == $today) ? '' : ($raid_day . ', ')) . $start . SP . getTranslation('to') . SP . $end . CR . CR; + + // Pokemon is an egg? + $keys_text = ''; + if(in_array($resolved_boss['pokedex_id'], EGGS)) { + $keys_text = EMOJI_EGG . SP; + } + $keys_text .= ($raid['ex_gym'] === 1 ? EMOJI_STAR . SP : '') . $gym_name; + + $keys[] = button($keys_text, ['raids_list', 'r' => $raid['id']]); +} +$keys[] = button(getTranslation('done'), ['exit', 'd' => '1']); - // Build message. - $msg = '' . getTranslation('list_all_active_raids') . ':' . CR; - $msg .= $text; - $msg .= '' . getTranslation('select_gym_name') . '' . CR; - } +// Get the inline key array. +$keys = inline_key_array($keys, 1); -// No active raids -} else { - $msg = '' . getTranslation('no_active_raids_found') . ''; -} +// Build message. +$msg = '' . getTranslation('list_all_active_raids') . ':' . CR; +$msg .= $text; +$msg .= '' . getTranslation('select_gym_name') . '' . CR; // Send message. -send_message($update['message']['chat']['id'], $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); -?> +send_message(create_chat_object([$update['message']['chat']['id']]), $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); diff --git a/commands/listall.php b/commands/listall.php index e109fcae..4a286b89 100644 --- a/commands/listall.php +++ b/commands/listall.php @@ -1,4 +1,5 @@ accessCheck('listall'); // Set keys. -$keys_and_gymarea = raid_edit_gyms_first_letter_keys('list_by_gym', false, false, 'listall', 'list_raid'); +$gymarea = resolveDefaultGymarea($botUser->userId); +$keys_and_gymarea = gymMenu('list', false, 1, false, $gymarea); $keys = $keys_and_gymarea['keys']; -$keys[] = [ - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] -]; // Set message. $msg = '' . getTranslation('list_all_active_raids') . '' . CR; -$msg.= (($keys_and_gymarea['gymarea_name'] != '') ? getTranslation('current_gymarea') . ': ' . $keys_and_gymarea['gymarea_name'] . CR: ''); -if($config->ENABLE_GYM_AREAS) { - if($keys_and_gymarea['gymarea_name'] == '') { - $msg .= '' . getTranslation('select_gym_area') . '' . CR; - }else { - if($keys_and_gymarea['letters']) { - $msg .= '' . getTranslation('select_gym_first_letter_or_gym_area') . '' . CR; - }else { - $msg .= '' . getTranslation('select_gym_name_or_gym_area') . '' . CR; - } - } -}elseif($keys_and_gymarea['letters']) { - $msg .= '' . getTranslation('select_gym_first_letter') . '' . CR; -}else { - $msg .= '' . getTranslation('select_gym_name') . '' . CR; -} +$msg.= $keys_and_gymarea['gymareaTitle']; // Send message. -send_message($update['message']['chat']['id'], $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true], 'disable_web_page_preview' => 'true']); - -?> +send_message(create_chat_object([$update['message']['chat']['id']]), $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true], 'disable_web_page_preview' => 'true']); diff --git a/commands/overview.php b/commands/overview.php index 6d2fbac5..cb60f82d 100644 --- a/commands/overview.php +++ b/commands/overview.php @@ -7,29 +7,15 @@ //debug_log($data); // Check access. -bot_access_check($update, 'overview'); - -// Init empty keys array. -$keys = []; +$botUser->accessCheck('overview'); // Create keys array. -$keys = [ - [ - [ - 'text' => getTranslation('overview_share'), - 'callback_data' => '0:overview_share:0' - ], - [ - 'text' => getTranslation('overview_delete'), - 'callback_data' => '0:overview_delete:0' - ] - ] -]; +$keys[][] = button(getTranslation('overview_share'), 'overview_share'); +$keys[][] = button(getTranslation('overview_delete'), 'overview_delete'); +$keys[][] = button(getTranslation('abort'), ['exit', 'd' => '0']); // Set message. $msg = '' . getTranslation('raids_share_overview') . ':'; // Send message. -send_message($update['message']['chat']['id'], $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); - -?> +send_message(create_chat_object([$update['message']['chat']['id']]), $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); diff --git a/commands/pokedex.php b/commands/pokedex.php index b8a05fd4..677f5a86 100644 --- a/commands/pokedex.php +++ b/commands/pokedex.php @@ -1,13 +1,14 @@ accessCheck('pokedex'); // Get pokemon name or dex id. $pokemon = trim(substr($update['message']['text'], 8)); @@ -17,74 +18,36 @@ // Check if we recived pokemon name or dex id. if(!empty($pokemon)) { - // Pokedex_id received? - if(is_numeric($pokemon)) { - $pokedex_id = $pokemon; - // Pokemon name received? - } else { - $pokemon_name_form = get_pokemon_id_by_name($pokemon); - $pokedex_id = explode("-", $pokemon_name_form)[0]; - } - $statement = $dbh->prepare("SELECT pokedex_id, pokemon_form_id FROM pokemon WHERE pokedex_id = :pokedex_id"); - $statement->execute([":pokedex_id" => $pokedex_id]); - while ($pokemon = $statement->fetch()) { - $keys[] = [ - [ - 'text' => get_local_pokemon_name($pokemon['pokedex_id'], $pokemon['pokemon_form_id']), - 'callback_data' => $pokemon['pokedex_id'] . '-' . $pokemon['pokemon_form_id'] . ':pokedex_edit_pokemon:0' - ] - ]; - } - // Set message. - $msg = '' . getTranslation('pokedex_edit_pokemon') . ''; + // Pokedex_id received? + if(is_numeric($pokemon)) { + $pokedex_id = $pokemon; + // Pokemon name received? + } else { + $pokemon_name_form = get_pokemon_id_by_name($pokemon); + $pokedex_id = $pokemon_name_form[0]; + } + $query = my_query('SELECT pokedex_id, pokemon_form_id FROM pokemon WHERE pokedex_id = :pokedex_id', [':pokedex_id' => $pokedex_id]); + while ($pokemon = $query->fetch()) { + $keys[][] = button(get_local_pokemon_name($pokemon['pokedex_id'], $pokemon['pokemon_form_id']), ['pokedex_edit_pokemon', 'p' => $pokemon['pokedex_id'] . '-' . $pokemon['pokemon_form_id']]); + } + // Set message. + $msg = '' . getTranslation('pokedex_edit_pokemon') . ''; } if(count($keys) == 0 ) { - $query = my_query("SELECT * FROM pokemon WHERE pokedex_id='9995'"); // A simple check to see if pokemon table has all the necessary data in it - if($query->rowCount() > 0) { - // Create keys array. - $keys = [ - [ - [ - 'text' => getTranslation('pokedex_raid_pokemon'), - 'callback_data' => '0:pokedex_list_raids:0' - ] - ], - [ - [ - 'text' => getTranslation('edit_pokemon'), - 'callback_data' => '0:pokedex:0' - ] - ], - [ - [ - 'text' => getTranslation('disable_raid_level'), - 'callback_data' => '0:pokedex_disable_raids:0' - ] - ], - [ - [ - 'text' => getTranslation('import'), - 'callback_data' => '0:pokedex_import:0' - ] - ] - ]; - } - $keys[][] = [ - 'text' => getTranslation('update_pokemon_table'), - 'callback_data' => '0:getdb:0' - ]; - // Set message. - $msg = '' . getTranslation('pokedex_start') . ':'; + $query = my_query('SELECT id FROM pokemon WHERE pokedex_id = 9999 and pokemon_form_id = 0'); // A simple check to see if pokemon table has all the necessary data in it + if($query->rowCount() > 0) { + // Create keys array. + $keys[][] = button(getTranslation('pokedex_raid_pokemon'), 'pokedex_list_raids'); + $keys[][] = button(getTranslation('edit_pokemon'), 'pokedex'); + $keys[][] = button(getTranslation('disable_raid_level'), 'pokedex_disable_raids'); + $keys[][] = button(getTranslation('import'), 'pokedex_import'); + } + $keys[][] = button(getTranslation('update_pokemon_table'), 'getdb'); + // Set message. + $msg = '' . getTranslation('pokedex_start') . ':'; } -$keys[] = [ - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] -]; +$keys[][] = button(getTranslation('abort'), 'exit'); // Send message. -send_message($update['message']['chat']['id'], $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); - -?> +send_message(create_chat_object([$update['message']['chat']['id']]), $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); diff --git a/commands/pokemon.php b/commands/pokemon.php index 7058f2b2..f58ab5e7 100644 --- a/commands/pokemon.php +++ b/commands/pokemon.php @@ -6,101 +6,70 @@ //debug_log($update); //debug_log($data); -// Check access. -bot_access_check($update, 'access-bot'); +if($botUser->accessCheck('pokemon-own', true)) { + $userRestriction = 'AND raids.user_id = ?'; + $binds = [$update['message']['chat']['id']]; +}elseif($botUser->accessCheck('pokemon-all', true)) { + $userRestriction = ''; + $binds = []; +}else { + $botUser->denyAccess(); +} -// Count results. -$count = 0; +$query = my_query(' + SELECT raids.*, gyms.gym_name + FROM raids + LEFT JOIN gyms + ON raids.gym_id = gyms.id + WHERE raids.end_time > UTC_TIMESTAMP + ' . $userRestriction . ' + ORDER BY raids.end_time ASC + LIMIT 20 +', $binds); + +if($query->rowCount() == 0) { + $msg = '' . getTranslation('no_active_raids_found') . ''; + send_message(create_chat_object([$update['message']['chat']['id']]), $msg); + exit; +} // Init text and keys. $text = ''; $keys = []; -try { - - $query = ' - SELECT - raids.*, gyms.lat , - gyms.lon , - gyms.address , - gyms.gym_name , - gyms.ex_gym , - users. NAME - FROM - raids - LEFT JOIN gyms ON raids.gym_id = gyms.id - LEFT JOIN users ON raids.user_id = users.user_id - WHERE - raids.end_time > UTC_TIMESTAMP - ORDER BY - raids.end_time ASC - LIMIT 20 - '; - $statement = $dbh->prepare( $query ); - $statement->execute(); - while ($row = $statement->fetch()) { - // Get times. - $now = utcnow(); - $today = dt2date($now); - $raid_day = dt2date($row['start_time']); - $start = dt2time($row['start_time']); - $end = dt2time($row['end_time']); - - // Split pokemon and form to get the pokedex id. - $pokedex_id = explode('-', $row['pokemon'])[0]; - - // Pokemon is an egg? - $eggs = $GLOBALS['eggs']; - if(in_array($pokedex_id, $eggs)) { - $keys_text = EMOJI_EGG . SP . $row['gym_name']; - } else { - $keys_text = $row['gym_name']; - } - - // Set text and keys. - $text .= $row['gym_name'] . CR; - $text .= get_local_pokemon_name($row['pokemon'], $row['pokemon_form']) . SP . '—' . SP . (($raid_day == $today) ? '' : ($raid_day . ', ')) . $start . SP . getTranslation('to') . SP . $end . CR . CR; - $keys[] = array( - 'text' => $keys_text, - 'callback_data' => $row['id'] . ':raid_edit_poke:' . $row['level'], - ); - - // Counter++ - $count = $count + 1; - } +while ($row = $query->fetch()) { + // Get times. + $now = utcnow(); + $today = dt2date($now); + $raid_day = dt2date($row['start_time']); + $start = dt2time($row['start_time']); + $end = dt2time($row['end_time']); + + // Split pokemon and form to get the pokedex id. + $pokedex_id = explode('-', $row['pokemon'])[0]; + + // Pokemon is an egg? + $keys_text = $row['gym_name']; + if(in_array($pokedex_id, EGGS)) { + $keys_text = EMOJI_EGG . SP . $row['gym_name']; + } + + // Set text and keys. + $text .= $row['gym_name'] . CR; + $text .= get_local_pokemon_name($row['pokemon'], $row['pokemon_form']) . SP . '—' . SP . (($raid_day == $today) ? '' : ($raid_day . ', ')) . $start . SP . getTranslation('to') . SP . $end . CR . CR; + $keys[] = button($keys_text, ['raid_edit_poke', 'r' => $row['id'], 'rl' => $row['level']]); } -catch (PDOException $exception) { - error_log($exception->getMessage()); - $dbh = null; - exit; -} - -// Set message. -if($count == 0) { - $msg = '' . getTranslation('no_active_raids_found') . ''; -} else { - // Get the inline key array. - $keys = inline_key_array($keys, 1); +// Get the inline key array. +$keys = inline_key_array($keys, 1); - // Add exit key. - $keys[] = [ - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ]; +// Add exit key. +$keys[][] = button(getTranslation('abort'), 'exit'); - // Build message. - $msg = '' . getTranslation('list_all_active_raids') . ':' . CR; - $msg .= $text; - $msg .= '' . getTranslation('select_gym_name') . '' . CR; -} - -// Build callback message string. -$callback_response = 'OK'; +// Build message. +$msg = '' . getTranslation('list_all_active_raids') . ':' . CR; +$msg .= $text; +$msg .= '' . getTranslation('select_gym_name') . '' . CR; // Send message. -send_message($update['message']['chat']['id'], $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); - -?> +send_message(create_chat_object([$update['message']['chat']['id']]), $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); diff --git a/commands/raid_from_webhook.php b/commands/raid_from_webhook.php index e527d823..b63c668e 100644 --- a/commands/raid_from_webhook.php +++ b/commands/raid_from_webhook.php @@ -1,361 +1,418 @@ registerCounter($namespace, 'webhook_raids_received_total', 'Total raids received via webhook'); - $webhook_raids_accepted_total = $metrics->registerCounter($namespace, 'webhook_raids_accepted_total', 'Total raids received & accepted via webhook'); - $webhook_raids_posted_total = $metrics->registerCounter($namespace, 'webhook_raids_posted_total', 'Total raids posted automatically'); +if($metrics) { + $webhook_raids_received_total = $metrics->registerCounter($namespace, 'webhook_raids_received_total', 'Total raids received via webhook'); + $webhook_raids_accepted_total = $metrics->registerCounter($namespace, 'webhook_raids_accepted_total', 'Total raids received & accepted via webhook'); + $webhook_raids_posted_total = $metrics->registerCounter($namespace, 'webhook_raids_posted_total', 'Total raids posted automatically'); } function isPointInsidePolygon($point, $vertices) { - $i = 0; - $j = 0; - $c = 0; - $count_vertices = count($vertices); - for ($i = 0, $j = $count_vertices-1 ; $i < $count_vertices; $j = $i++) { - if ((($vertices[$i]['y'] > $point['y'] != ($vertices[$j]['y'] > $point['y'])) && ($point['x'] < ($vertices[$j]['x'] - $vertices[$i]['x']) * ($point['y'] - $vertices[$i]['y']) / ($vertices[$j]['y'] - $vertices[$i]['y']) + $vertices[$i]['x']) ) ) { - $c = !$c; - } + $i = $j = $c = 0; + $count_vertices = count($vertices); + for($i = 0, $j = $count_vertices-1 ; $i < $count_vertices; $j = $i++) { + if((($vertices[$i]['y'] > $point['y'] != ($vertices[$j]['y'] > $point['y'])) && ($point['x'] < ($vertices[$j]['x'] - $vertices[$i]['x']) * ($point['y'] - $vertices[$i]['y']) / ($vertices[$j]['y'] - $vertices[$i]['y']) + $vertices[$i]['x']) ) ) { + $c = !$c; } - return $c; + } + return $c; } // Geofences $geofences = false; -if (file_exists(CONFIG_PATH . '/geoconfig.json')) { - $raw = file_get_contents(CONFIG_PATH . '/geoconfig.json'); - $geofences = json_decode($raw, true); - $geofence_polygons = []; - foreach($geofences as $geofence) { - foreach ($geofence['path'] as $geopoint) { - $geofence_polygons[$geofence['id']][] = ['x' => $geopoint[0], 'y' => $geopoint[1]]; - } +if(file_exists(botSpecificConfigFile('geoconfig.json'))) { + $raw = file_get_contents(botSpecificConfigFile('geoconfig.json')); + $geofences = json_decode($raw, true); + $geofence_polygons = []; + foreach($geofences as $geofence) { + foreach($geofence['path'] as $geopoint) { + $geofence_polygons[$geofence['id']][] = ['x' => $geopoint[0], 'y' => $geopoint[1]]; } + } +} + +$cleanup_data = []; +if(!empty($config->WEBHOOK_CHATS_BY_POKEMON[0])) { + // Fetch cleanup info for later use + $query_cleanup = my_query('SELECT raid_id, chat_id FROM cleanup'); + while($row = $query_cleanup->fetch()) { + $cleanup_data[$row['raid_id']][] = $row['chat_id']; + } } // Telegram JSON array. $tg_json = []; debug_log(count($update),"Received raids:"); -if ($metrics){ - $webhook_raids_received_total->incBy(count($update)); +if($metrics) { + $webhook_raids_received_total->incBy(count($update)); } -foreach ($update as $raid) { - $level = $raid['message']['level']; - $pokemon = $raid['message']['pokemon_id']; - $exclude_raid_levels = explode(',', $config->WEBHOOK_EXCLUDE_RAID_LEVEL); - $exclude_pokemons = explode(',', $config->WEBHOOK_EXCLUDE_POKEMON); - if ((!empty($level) && in_array($level, $exclude_raid_levels)) || (!empty($pokemon) && in_array($pokemon, $exclude_pokemons))) { - debug_log($pokemon.' Tier: '.$level,'Ignoring raid, the pokemon or raid level is excluded:'); - continue; - } +foreach($update as $raid) { + if($raid['type'] != 'raid') continue; + // Skip posting if create only -mode is set or raid time is greater than value set in config + $no_auto_posting = ($config->WEBHOOK_CREATE_ONLY or ($raid['message']['end']-$raid['message']['start']) > ($config->WEBHOOK_EXCLUDE_AUTOSHARE_DURATION * 60)); + + $level = $raid['message']['level']; + $pokemon = $raid['message']['pokemon_id']; + $exclude_raid_levels = explode(',', $config->WEBHOOK_EXCLUDE_RAID_LEVEL); + $exclude_pokemons = explode(',', $config->WEBHOOK_EXCLUDE_POKEMON); + if((!empty($level) && in_array($level, $exclude_raid_levels)) || (!empty($pokemon) && in_array($pokemon, $exclude_pokemons))) { + debug_log($pokemon.' Tier: '.$level,'Ignoring raid, the pokemon or raid level is excluded:'); + continue; + } - $gym_name = $raid['message']['name']; - if ($config->WEBHOOK_EXCLUDE_UNKNOWN && $gym_name === 'unknown') { - debug_log($raid['message']['gym_id'],'Ignoring raid, the gym name is unknown and WEBHOOK_EXCLUDE_UNKNOWN says to ignore. id:'); - continue; + $gym_name = isset($raid['message']['name']) ? $raid['message']['name'] : $raid['message']['gym_name']; + if($config->WEBHOOK_EXCLUDE_UNKNOWN && ($gym_name === 'unknown' || $gym_name === 'Unknown')) { + debug_log($raid['message']['gym_id'],'Ignoring raid, the gym name is unknown and WEBHOOK_EXCLUDE_UNKNOWN says to ignore. id:'); + continue; + } + $gym_lat = $raid['message']['latitude']; + $gym_lon = $raid['message']['longitude']; + $gym_id = $raid['message']['gym_id']; + $gym_img_url = isset($raid['message']['url']) ? $raid['message']['url'] : $raid['message']['gym_url']; + $gym_is_ex = isset($raid['message']['is_ex_raid_eligible']) ? ( $raid['message']['is_ex_raid_eligible'] ? 1 : 0 ) : ( $raid['message']['ex_raid_eligible'] ? 1 : 0 ); + $gym_internal_id = 0; + + // Check geofence, if available, and skip current raid if not inside any fence + if($geofences != false) { + $insideGeoFence = false; + $inside_geofences = []; + $point = ['x' => $gym_lat, 'y' => $gym_lon]; + foreach($geofence_polygons as $geofence_id => $polygon) { + if(isPointInsidePolygon($point, $polygon)) { + $inside_geofences[] = $geofence_id; + $insideGeoFence = true; + debug_log($geofence_id,'Raid inside geofence:'); + } } - $gym_lat = $raid['message']['latitude']; - $gym_lon = $raid['message']['longitude']; - $gym_id = $raid['message']['gym_id']; - $gym_img_url = $raid['message']['url']; - $gym_is_ex = ( $raid['message']['is_ex_raid_eligible'] ? 1 : 0 ); - $gym_internal_id = 0; - - // Check geofence, if available, and skip current raid if not inside any fence - if ($geofences != false) { - $insideGeoFence = false; - $inside_geofences = []; - $point = ['x' => $gym_lat, 'y' => $gym_lon]; - foreach ($geofence_polygons as $geofence_id => $polygon) { - if (isPointInsidePolygon($point, $polygon)) { - $inside_geofences[] = $geofence_id; - $insideGeoFence = true; - debug_log($geofence_id,'Raid inside geofence:'); - } - } - if ($insideGeoFence === false) { - debug_log($gym_name,'Ignoring raid, not inside any geofence:'); - continue; - } + if($insideGeoFence === false) { + debug_log($gym_name,'Ignoring raid, not inside any geofence:'); + continue; } + } - // Create gym if it doesn't exists, otherwise update gym info. - try { - $query = ' - INSERT INTO gyms (lat, lon, gym_name, gym_id, ex_gym, img_url, show_gym) - VALUES (:lat, :lon, :gym_name, :gym_id, :ex_gym, :img_url, 1) - ON DUPLICATE KEY UPDATE - lat = :lat, - lon = :lon, - gym_name = :gym_name, - ex_gym = :ex_gym, - img_url = :img_url - '; - $statement = $dbh->prepare( $query ); - $statement->execute([ - 'lat' => $gym_lat, - 'lon' => $gym_lon, - 'gym_name' => $gym_name, - 'gym_id' => $gym_id, - 'ex_gym' => $gym_is_ex, - 'img_url' => $gym_img_url - ]); - if($statement->rowCount() == 1) { - $gym_internal_id = $dbh->lastInsertId(); - debug_log($gym_internal_id, 'New gym '.$gym_name.' created with internal id of:'); - }else { - $statement = $dbh->prepare('SELECT id FROM gyms WHERE gym_id LIKE :gym_id LIMIT 1'); - $statement->execute(['gym_id'=>$gym_id]); - $gym_internal_id = $statement->fetch()['id']; - debug_log($gym_internal_id, 'Gym info updated. Internal id:'); - } - } - catch (PDOException $exception) { - error_log($exception->getMessage()); - $dbh = null; - exit; + // Create gym if it doesn't exists, otherwise update gym info. + $query = my_query(' + INSERT INTO gyms (lat, lon, gym_name, gym_id, ex_gym, img_url, show_gym) + VALUES (:lat, :lon, :gym_name, :gym_id, :ex_gym, :img_url, 1) + ON DUPLICATE KEY UPDATE + lat = :lat, + lon = :lon, + gym_name = :gym_name, + ex_gym = :ex_gym, + img_url = :img_url + ',[ + 'lat' => $gym_lat, + 'lon' => $gym_lon, + 'gym_name' => $gym_name, + 'gym_id' => $gym_id, + 'ex_gym' => $gym_is_ex, + 'img_url' => $gym_img_url, + ]); + if($query->rowCount() == 1) { + $gym_internal_id = $dbh->lastInsertId(); + debug_log($gym_internal_id, 'New gym '.$gym_name.' created with internal id of:'); + }else { + $statement = my_query('SELECT id FROM gyms WHERE gym_id LIKE :gym_id LIMIT 1',['gym_id'=>$gym_id]); + $gym_internal_id = $statement->fetch()['id']; + debug_log($gym_internal_id, 'Gym info updated. Internal id:'); + } + + // Create raid if not exists otherwise update if changes are detected + + // Raid pokemon form + // Use negated evolution id instead of form id if present + if(isset($raid['message']['evolution']) && $raid['message']['evolution'] > 0) { + $form = 0 - $raid['message']['evolution']; + }else { + $form = isset($raid['message']['form']) ? $raid['message']['form'] : 0; + } + + // Raid pokemon gender + $gender = 0; + if( isset($raid['message']['gender']) ) { + $gender = $raid['message']['gender']; + } + // Raid pokemon costume + $costume = 0; + if( isset($raid['message']['costume']) ) { + $costume = $raid['message']['costume']; + } + + // Raid pokemon moveset + $move_1 = 0; + $move_2 = 0; + if($pokemon < 9900) { + $move_1 = $raid['message']['move_1']; + $move_2 = $raid['message']['move_2']; + } + + // Raid start and endtimes + $spawn = (isset($raid['message']['spawn'])) ? gmdate('Y-m-d H:i:s',$raid['message']['spawn']) : gmdate('Y-m-d H:i:s', ($raid['message']['start'] - $config->RAID_EGG_DURATION*60)); + $start = gmdate('Y-m-d H:i:s',$raid['message']['start']); + $end = gmdate('Y-m-d H:i:s',$raid['message']['end']); + + // Gym team + $team = $raid['message']['team_id']; + if(!empty($team)) { + switch ($team) { + case (1): + $team = 'mystic'; + break; + case (2): + $team = 'valor'; + break; + case (3): + $team = 'instinct'; + break; } + } - // Create raid if not exists otherwise update if changes are detected + // Insert new raid or update existing raid/ex-raid? + $activeRaid = active_raid_duplication_check($gym_internal_id, $level, true); + $raid_id = (is_array($activeRaid)) ? $activeRaid['id'] : $activeRaid; - // Raid pokemon form - // Use negated evolution id instead of form id if present - if(isset($raid['message']['evolution']) && $raid['message']['evolution'] > 0) { - $form = 0 - $raid['message']['evolution']; + $send_updates = false; + + // Raid exists, do updates! + if( $raid_id > 0 ) { + debug_log($gym_name, 'Raid already in DB for gym:'); + // Update database + $statement = my_query(' + UPDATE raids + SET + pokemon = :pokemon, + pokemon_form = :pokemon_form, + spawn = :spawn, + start_time = :start_time, + end_time = :end_time, + gym_team = :gym_team, + level = :level, + move1 = :move1, + move2 = :move2, + gender = :gender, + costume = :costume + WHERE + id = :id + ',[ + 'pokemon' => $pokemon, + 'pokemon_form' => $form, + 'spawn' => $spawn, + 'start_time' => $start, + 'end_time' => $end, + 'gym_team' => $team, + 'level' => $level, + 'move1' => $move_1, + 'move2' => $move_2, + 'gender' => $gender, + 'costume' => $costume, + 'id' => $raid_id, + ]); + + // If update was needed, send them to TG + if($statement->rowCount() > 0) { + $send_updates = true; + debug_log($raid_id, 'Raid updated:'); }else { - $form = isset($raid['message']['form']) ? $raid['message']['form'] : 0; + debug_log($gym_name,'Nothing had changed for raid at gym:'); + continue; } + }else { + // Create Raid and send messages + debug_log($gym_name, 'Raid not in DB yet, creating for gym:'); + my_query(' + INSERT INTO raids (pokemon, pokemon_form, user_id, spawn, start_time, end_time, gym_team, gym_id, level, move1, move2, gender, costume) + VALUES (:pokemon, :pokemon_form, :user_id, :spawn, :start_time, :end_time, :gym_team, :gym_id, :level, :move1, :move2, :gender, :costume) + ',[ + 'pokemon' => $pokemon, + 'pokemon_form' => $form, + 'user_id' => $config->WEBHOOK_CREATOR, + 'spawn' => $spawn, + 'start_time' => $start, + 'end_time' => $end, + 'gym_team' => $team, + 'gym_id' => $gym_internal_id, + 'level' => $level, + 'move1' => $move_1, + 'move2' => $move_2, + 'gender' => $gender, + 'costume' => $costume + ]); + $raid_id = $dbh->lastInsertId(); + debug_log($raid_id, 'New raid created, raid id:'); - // Raid pokemon gender - $gender = 0; - if ( isset($raid['message']['gender']) ) { - $gender = $raid['message']['gender']; - } - // Raid pokemon costume - $costume = 0; - if ( isset($raid['message']['costume']) ) { - $costume = $raid['message']['costume']; + if($metrics) { + $webhook_raids_accepted_total->inc(); } - // Raid pokemon moveset - $move_1 = 0; - $move_2 = 0; - if ($pokemon < 9900) { - $move_1 = $raid['message']['move_1']; - $move_2 = $raid['message']['move_2']; + if($no_auto_posting) { + debug_log($gym_name,'Not autoposting raid, WEBHOOK_CREATE_ONLY is set to true or raids duration is over the WEBHOOK_EXCLUDE_AUTOSHARE_DURATION threshold:'); + continue; } + } - // Raid start and endtimes - $spawn = (isset($raid['message']['spawn'])) ? gmdate('Y-m-d H:i:s',$raid['message']['spawn']) : gmdate('Y-m-d H:i:s', ($raid['message']['start'] - $config->RAID_EGG_DURATION*60)); - $start = gmdate('Y-m-d H:i:s',$raid['message']['start']); - $end = gmdate('Y-m-d H:i:s',$raid['message']['end']); - - // Gym team - $team = $raid['message']['team_id']; - if (! empty($team)) { - switch ($team) { - case (1): - $team = 'mystic'; - break; - case (2): - $team = 'valor'; - break; - case (3): - $team = 'instinct'; - break; - } - } + // Query missing data needed to construct the raid poll + $query_missing = my_query(' + SELECT + gyms.lat, gyms.lon, gyms.address, gyms.gym_name, gyms.ex_gym, gyms.gym_note, + users.*, + TIME_FORMAT(TIMEDIFF(:raid_end_time, UTC_TIMESTAMP()) + INTERVAL 1 MINUTE, \'%k:%i\') AS t_left + FROM gyms + LEFT JOIN (SELECT users.name, users.trainername, users.nick FROM users WHERE users.user_id = :user_id) as users on 1 + WHERE gyms.id = :gym_internal_id + LIMIT 1 + ',[ + 'raid_end_time' => $end, + 'user_id' => $config->WEBHOOK_CREATOR, + 'gym_internal_id' => $gym_internal_id, + ]); - // Insert new raid or update existing raid/ex-raid? - $raid_id = active_raid_duplication_check($gym_internal_id); - - $send_updates = false; - - // Raid exists, do updates! - if ( $raid_id > 0 ) { - // Update database - try { - $query = ' - UPDATE raids - SET - pokemon = :pokemon, - pokemon_form = :pokemon_form, - gym_team = :gym_team, - move1 = :move1, - move2 = :move2, - gender = :gender, - costume = :costume - WHERE - id = :id - '; - $execute_array = [ - 'pokemon' => $pokemon, - 'pokemon_form' => $form, - 'gym_team' => $team, - 'move1' => $move_1, - 'move2' => $move_2, - 'gender' => $gender, - 'costume' => $costume, - 'id' => $raid_id - ]; - $statement = $dbh->prepare( $query ); - $statement->execute($execute_array); - } - catch (PDOException $exception) { - error_log($exception->getMessage()); - $dbh = null; - exit; - } - // If update was needed, send them to TG - if($statement->rowCount() > 0) { - $send_updates = true; - debug_log($raid_id, 'Raid updated:'); - }else { - debug_log($gym_name,'Nothing was updated, moving on:'); - continue; - } - }else { - // Create Raid and send messages - try { - $query = ' - INSERT INTO raids (pokemon, pokemon_form, user_id, spawn, start_time, end_time, gym_team, gym_id, level, move1, move2, gender, costume) - VALUES (:pokemon, :pokemon_form, :user_id, :spawn, :start_time, :end_time, :gym_team, :gym_id, :level, :move1, :move2, :gender, :costume) - '; - $execute_array = [ - 'pokemon' => $pokemon, - 'pokemon_form' => $form, - 'user_id' => $config->WEBHOOK_CREATOR, - 'spawn' => $spawn, - 'start_time' => $start, - 'end_time' => $end, - 'gym_team' => $team, - 'gym_id' => $gym_internal_id, - 'level' => $level, - 'move1' => $move_1, - 'move2' => $move_2, - 'gender' => $gender, - 'costume' => $costume - ]; - $statement = $dbh->prepare( $query ); - $statement->execute($execute_array); - $raid_id = $dbh->lastInsertId(); - debug_log($raid_id, 'New raid created, raid id:'); - } - catch (PDOException $exception) { - error_log($exception->getMessage()); - $dbh = null; - exit; - } - if ($metrics){ - $webhook_raids_accepted_total->inc(); - } + $missing_raid_data = $query_missing->fetch(); - // Skip posting if create only -mode is set or raid time is greater than value set in config - if ($config->WEBHOOK_CREATE_ONLY or ($raid['message']['end']-$raid['message']['start']) > ($config->WEBHOOK_EXCLUDE_AUTOSHARE_DURATION * 60) ) { - debug_log($gym_name,'Not autoposting raid, WEBHOOK_CREATE_ONLY is set to true or raids duration is over the WEBHOOK_EXCLUDE_AUTOSHARE_DURATION threshold:'); - continue; - } + $resolved_boss = resolve_raid_boss($pokemon, $form, $spawn, $level); + $wasBossUpdated = false; + if(is_array($activeRaid)) { + $resolvedOldBoss = resolve_raid_boss($activeRaid['pokemon'], $activeRaid['pokemon_form'], $activeRaid['spawn'], $activeRaid['level']); + if( + $resolved_boss['pokedex_id'] != $resolvedOldBoss['pokedex_id'] or + $resolved_boss['pokemon_form_id'] != $resolvedOldBoss['pokemon_form_id'] + ) { + $wasBossUpdated = true; } + } - // Query missing data needed to construct the raid poll - try { - $query_missing = ' - SELECT - gyms.lat, gyms.lon, gyms.address, gyms.gym_name, gyms.ex_gym, gyms.gym_note, - users.*, - TIME_FORMAT(TIMEDIFF(:raid_end_time, UTC_TIMESTAMP()) + INTERVAL 1 MINUTE, \'%k:%i\') AS t_left - FROM gyms - LEFT JOIN (SELECT users.name, users.trainername, users.nick FROM users WHERE users.user_id = :user_id) as users on 1 - WHERE gyms.id = :gym_internal_id - LIMIT 1 - '; - $execute_array_missing = [ - 'raid_end_time' => $end, - 'user_id' => $config->WEBHOOK_CREATOR, - 'gym_internal_id' => $gym_internal_id, - ]; - - $statement_missing = $dbh->prepare($query_missing); - $statement_missing->execute($execute_array_missing); - $missing_raid_data = $statement_missing->fetch(); - - $resolved_boss = resolve_raid_boss($pokemon, $form, $spawn, $level); - - // Combine resulting data with stuff received from webhook to create a complete raid array - $raid = array_merge($missing_raid_data, [ - 'id' => $raid_id, - 'user_id' => $config->WEBHOOK_CREATOR, - 'spawn' => $spawn, - 'pokemon' => $resolved_boss['pokedex_id'], - 'pokemon_form' => $resolved_boss['pokemon_form_id'], - 'start_time' => $start, - 'end_time' => $end, - 'gym_team' => $team, - 'gym_id' => $gym_internal_id, - 'level' => $level, - 'move1' => $move_1, - 'move2' => $move_2, - 'gender' => $gender, - 'costume' => $costume, - 'event' => NULL, - 'event_note' => NULL, - 'event_name' => NULL, - 'event_description' => NULL, - 'event_vote_key_mode' => NULL, - 'event_time_slots' => NULL, - 'event_raid_duration' => NULL, - 'event_hide_raid_picture' => NULL, - 'event_poll_template' => NULL, - ]); - } - catch (PDOException $exception) { - error_log($exception->getMessage()); - $dbh = null; - exit; - } - if($send_updates == true) { - require_once(LOGIC_PATH .'/update_raid_poll.php'); - $update = update_raid_poll($raid_id, $raid, false, $tg_json, false); // update_raid_poll() will return false if the raid isn't shared to any chat - if($update != false) $tg_json = $update; + // Combine resulting data with stuff received from webhook to create a complete raid array + $raid = array_merge($missing_raid_data, [ + 'id' => $raid_id, + 'user_id' => $config->WEBHOOK_CREATOR, + 'spawn' => $spawn, + 'pokemon' => $resolved_boss['pokedex_id'], + 'pokemon_form' => $resolved_boss['pokemon_form_id'], + 'start_time' => $start, + 'end_time' => $end, + 'gym_team' => $team, + 'gym_id' => $gym_internal_id, + 'level' => $level, + 'move1' => $move_1, + 'move2' => $move_2, + 'gender' => $gender, + 'costume' => $costume, + 'event' => NULL, + 'event_note' => NULL, + 'event_name' => NULL, + 'event_description' => NULL, + 'event_vote_key_mode' => NULL, + 'event_time_slots' => NULL, + 'event_raid_duration' => NULL, + 'event_hide_raid_picture' => NULL, + 'event_pokemon_title' => NULL, + 'event_poll_template' => NULL, + 'raid_ended' => 0, + 'shadow' => (in_array($level, RAID_LEVEL_SHADOW) ? 1 : 0), + ]); + + $chats_geofence = $chats_raidlevel = $webhook_chats = $chats_by_pokemon = $chats = []; + if($send_updates == true) { + // Update raid polls and send alerts of updates + require_once(LOGIC_PATH .'/update_raid_poll.php'); + $tg_json = update_raid_poll($raid_id, $raid, false, $tg_json, true); + if($wasBossUpdated) $tg_json = alarm($raid, false, 'new_boss', '', $tg_json); + // Post hatched Pokemon to their respective chats if configured + // Start share_chats backwards compatibility + if(!isset($config->CHATS_SHARE)) { + if(!empty($config->WEBHOOK_CHATS_BY_POKEMON[0]) && !$no_auto_posting) { + foreach($config->WEBHOOK_CHATS_BY_POKEMON as $rule) { + if(isset($rule['pokemon_id']) && $rule['pokemon_id'] == $pokemon && (!isset($rule['form_id']) or (isset($rule['form_id']) && $rule['form_id'] == $form))) { + foreach($rule['chats'] as $rule_chat) { + // If the raid isn't already posted to the chats specified in WEBHOOK_CHATS_BY_POKEMON, we add it to the array + if(!isset($cleanup_data[$raid_id]) or !in_array($rule_chat, $cleanup_data[$raid_id])) { + $chats_by_pokemon[] = create_chat_object([$rule_chat]); + } + } + } + } + } + // End chats_share backwards compatibility }else { - // Get chats to share to by raid level and geofence id - $chats_geofence = []; - if($geofences != false) { - foreach ($inside_geofences as $geofence_id) { - $const_geofence = 'WEBHOOK_CHATS_LEVEL_' . $level . '_' . $geofence_id; - $const_geofence_chats = $config->{$const_geofence}; - - if(!empty($const_geofence_chats)) { - $chats_geofence = explode(',', $const_geofence_chats); - } + if(!empty($config->CHATS_SHARE['webhook']['by_pokemon']) && !$no_auto_posting) { + foreach($config->CHATS_SHARE['webhook']['by_pokemon'] as $rule) { + if(isset($rule['pokemon_id']) && $rule['pokemon_id'] == $pokemon && (!isset($rule['form_id']) or (isset($rule['form_id']) && $rule['FORM_ID'] == $form))) { + foreach($rule['chats'] as $rule_chat) { + if(!isset($cleanup_data[$raid_id]) or !in_array($rule_chat['id'], $cleanup_data[$raid_id])) { + $chats_by_pokemon[] = $rule_chat; + } } + } } + } + } - // Get chats to share to by raid level - $const = 'WEBHOOK_CHATS_LEVEL_' . $level; - $const_chats = $config->{$const}; + if(empty($chats_by_pokemon)) continue; + }else { + // Start share_chats backwards compatibility + if(!isset($config->CHATS_SHARE)) { + // Get chats to share to by raid level and geofence id + if($geofences != false) { + foreach($inside_geofences as $geofence_id) { + $const_geofence = 'WEBHOOK_CHATS_LEVEL_' . $level . '_' . $geofence_id; + $const_geofence_chats = $config->{$const_geofence} ?? []; - $chats_raidlevel =[]; - if(!empty($const_chats)) { - $chats_raidlevel = explode(',', $const_chats); + if(!empty($const_geofence_chats)) { + $chats_geofence = explode(',', $const_geofence_chats); + } } + } - // Get chats - $webhook_chats = []; - if(!empty($config->WEBHOOK_CHATS_ALL_LEVELS)) { - $webhook_chats = explode(',', $config->WEBHOOK_CHATS_ALL_LEVELS); - } - $chats = array_merge($chats_geofence, $chats_raidlevel, $webhook_chats); + // Get chats to share to by raid level + $const = 'WEBHOOK_CHATS_LEVEL_' . $level; + $const_chats = $config->{$const} ?? []; + + if(!empty($const_chats)) { + $chats_raidlevel = explode(',', $const_chats); + } - require_once(LOGIC_PATH .'/send_raid_poll.php'); - if ($metrics){ - $webhook_raids_posted_total->inc(); + // Get chats + if(!empty($config->WEBHOOK_CHATS_ALL_LEVELS)) { + $webhook_chats = explode(',', $config->WEBHOOK_CHATS_ALL_LEVELS); + } + $chats_combined = array_merge($chats_geofence, $chats_raidlevel, $webhook_chats); + foreach($chats_combined as $chat) { + $chats[] = create_chat_object([$chat]); + } + // End chats_share backwards compatibility + }else { + if($geofences != false) { + foreach($inside_geofences as $geofence_id) { + $geofence_chats_all = $config->CHATS_SHARE['webhook']['geofences'][$geofence_id]['all'] ?? []; + $geofence_chats_by_level = $config->CHATS_SHARE['webhook']['geofences'][$geofence_id][$level] ?? []; + + if(!empty($geofence_chats_all)) { + $chats_geofence = array_merge($chats_geofence, $geofence_chats_all); + } + if(!empty($geofence_chats_by_level)) { + $chats_geofence = array_merge($chats_geofence, $geofence_chats_by_level); + } } - $tg_json = send_raid_poll($raid_id, $chats, $raid, $tg_json); + } + // Get chats to share to by raid level + $chats_raidlevel = $config->CHATS_SHARE['webhook'][$level] ?? []; + + // Get chats + $webhook_chats = $config->CHATS_SHARE['webhook']['all'] ?? []; + $chats = array_merge($chats_geofence, $chats_raidlevel, $webhook_chats); } -} + } + require_once(LOGIC_PATH .'/send_raid_poll.php'); + if($metrics) { + $webhook_raids_posted_total->inc(); + } + if(count($chats) > 0) { + $tg_json = send_raid_poll($raid_id, $chats, $raid, $tg_json); + }elseif(count($chats_by_pokemon) > 0) { + $tg_json = send_raid_poll($raid_id, $chats_by_pokemon, $raid, $tg_json); + } +} // Telegram multicurl request. curl_json_multi_request($tg_json); - -?> diff --git a/commands/set.php b/commands/set.php new file mode 100644 index 00000000..bf88f9e0 --- /dev/null +++ b/commands/set.php @@ -0,0 +1,180 @@ +accessCheck('config-set'); + +// Get config name and value. +$input = trim(substr($update['message']['text'], 4)); + +// Get delimiter count. +$count = substr_count($input, " "); + +// Get allowed telegram configs. +$allowed = explode(',', $config->ALLOWED_TELEGRAM_CONFIG); + +// Get config restrictions for boolean input +$allowed_bool = explode(',', $config->ALLOW_ONLY_TRUE_FALSE); + +// Get config restrictions for numeric input +$allowed_numbers = explode(',', $config->ALLOW_ONLY_NUMBERS); + +// Write to log. +debug_log('User submitted a telegram config change'); +debug_log('Allowed telegram configs: ' . $config->ALLOWED_TELEGRAM_CONFIG); +debug_log('Allow only boolean input: ' . $config->ALLOW_ONLY_TRUE_FALSE); +debug_log('Allow only numeric input: ' . $config->ALLOW_ONLY_NUMBERS); + +// 0 means we reset config option value to "" +if($count == 0) { + // Upper input. + $config_name = strtoupper($input); + $config_value = ''; + debug_log('Reset for the config value ' . $config_name . ' was requested by the user'); + +// 1 means we set the config option to the given value +} else if($count >= 1) { + // Config name and value. + $cfg_name_value = explode(' ', $input, 2); + $config_name = strtoupper($cfg_name_value[0]); + $config_value = $cfg_name_value[1]; + debug_log('Change for the config option ' . $config_name . ' was requested by the user'); + +// Set config_name to avoid undefined variable for if clause below. +} else { + $config_name = 'not_supported'; +} + +// Config +$cfile = botSpecificConfigFile('config.json'); +if(is_file($cfile)) { + $str = file_get_contents($cfile); + $json = json_decode($str, true); +} + +// Real config name or alias? +$alias = ''; +$afile = botSpecificConfigFile('alias.json'); +if(is_file($afile)) { + debug_log('Checking alias for config option ' . $config_name); + $astr = file_get_contents($afile); + // We compare always uppercase, so change str to upper + $astr = strtoupper($astr); + $ajson = json_decode($astr, true); + $alias = array_search($config_name, $ajson); + // Check for alias + if ($alias !== false) { + debug_log('Config option ' . $config_name . ' is an alias for ' . $alias); + $help = $config_name; + $config_name = strtoupper($alias); + $alias = $help; + } else { + debug_log('No alias found. Seems ' . $config_name . ' is the config option name'); + } +} + +// Assume restrictions. +$restrict = 'yes'; + +// Init additional error info. +$msg_error_info = ''; + +// Make sure it's allowed to update the value via telegram. +if(in_array($config_name, $allowed)) { + // Only bool? + if(in_array($config_name, $allowed_bool)) { + if(strcasecmp($config_value, 'true') == 0 || strcasecmp($config_value, 'false') == 0) { + $config_value = strtolower($config_value); + $restrict = 'no'; + } else if($config_value == 0 || $config_value == 1) { + $restrict = 'no'; + } else { + debug_log('Boolean value expected. Got this value: ' . $config_value); + $msg_error_info .= getTranslation('help_bool_expected'); + } + + + // Only numbers? + } else if(in_array($config_name, $allowed_numbers)) { + if(is_numeric($config_value)) { + $restrict = 'no'; + } else { + debug_log('Number expected. Got this value: ' . $config_value); + $msg_error_info .= getTranslation('help_number_expected'); + } + + // No restriction on input type. + } else { + $restrict = 'no'; + } +} + +// Update config. +if(in_array($config_name, $allowed) && $restrict == 'no') { + // Prepare data, replace " with ' + $config_value = str_replace('"', "'", $config_value); + $old_value = $json[$config_name]; + if($old_value === true) $old_value = 'true'; + elseif($old_value === false) $old_value = 'false'; + $json[$config_name] = $config_value; + $jsonString = json_encode($json, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT); + debug_log($config_name, 'CONFIG NAME:'); + debug_log($config_value, 'CONFIG VALUE:'); + + // Write to file. + if(json_last_error() === JSON_ERROR_NONE) { + file_put_contents(botSpecificConfigFile('config.json'), $jsonString); + $msg = getTranslation('config_updated') . ':' . CR . CR; + $msg .= '' . (empty($alias) ? $config_name : $alias) . '' . CR; + $msg .= getTranslation('old_value') . SP . $old_value . CR; + $msg .= getTranslation('new_value') . SP . $config_value . CR; + debug_log('Changed the config value for ' . $config_name . ' from ' . $old_value . ' to ' . $config_value); + } else { + $msg_error_info = getTranslation('internal_error'); + $msg = '' . getTranslation('invalid_input') . '' . (!empty($msg_error_info) ? (CR . $msg_error_info) : '') . CR . CR; + } + +// Tell user how to set config and what is allowed to be set by config. +} else { + $msg = '' . getTranslation('invalid_input') . '' . (!empty($msg_error_info) ? (CR . $msg_error_info) : '') . CR . CR; + $msg .= '' . getTranslation('config') . ':' . CR; + // Any configs allowed? + if(!empty($config->ALLOWED_TELEGRAM_CONFIG)) { + $msg .= '/set [' . getTranslation("config_option") . '] [' . getTranslation('option_value') . ']' . CR; + foreach($json as $cfg_name => $cfg_value) { + // Only allowed configs + if(in_array($cfg_name, $allowed)) { + // Is alias set? + $alias = ''; + if(isset($ajson[$cfg_name])){ + $alias = $ajson[$cfg_name]; + } + // Config name / Alias + value + $msg .= '/set' . SP . (empty($alias) ? $cfg_name : $alias) . SP . (empty($cfg_value) ? '' . getTranslation('no_value') . '' : $cfg_value); + + // Only bool? + if(in_array($cfg_name, $allowed_bool)) { + $msg .= SP . '(' . getTranslation('help_only_bool') . ')'; + + // Only numbers? + } else if(in_array($cfg_name, $allowed_numbers)) { + $msg .= SP . '(' . getTranslation('help_only_numbers') . ')'; + + // Any type + } + $msg .= CR; + } + } + } else { + $msg .= getTranslation('not_supported'); + } + debug_log('Unsupported request for a telegram config change: ' . $input); +} + +// Send message. +send_message(create_chat_object([$update['message']['chat']['id']]), $msg); diff --git a/commands/start.php b/commands/start.php index 23816ba8..67b450c2 100644 --- a/commands/start.php +++ b/commands/start.php @@ -1,99 +1,63 @@ accessCheck('create', true, $new_user); +if(!$access && !$new_user) { + if($botUser->accessCheck('list', true)){ debug_log('No access to create, will do a list instead'); require('list.php'); - exit; + }else { + $response_msg = '' . getTranslation('bot_access_denied') . ''; + send_message(create_chat_object([$update['message']['chat']['id']]), $response_msg); + } + exit; } -if($config->TUTORIAL_MODE && $new_user && (!$access or $access == 'BOT_ADMINS')) { - // Tutorial - if(is_file(ROOT_PATH . '/config/tutorial.php')) { - require_once(ROOT_PATH . '/config/tutorial.php'); - } - $msg = $tutorial[0]['msg_new']; - $keys = [ - [ - [ - 'text' => getTranslation("next"), - 'callback_data' => '0:tutorial:1' - ] - ] - ]; - $photo = $tutorial[0]['photo']; - send_photo($update['message']['from']['id'], $photo, $msg, $keys, ['disable_web_page_preview' => 'true'],false); -}else { - // Trim away everything before "/start " - $searchterm = $update['message']['text']; - $searchterm = substr($searchterm, 7); - debug_log($searchterm, 'SEARCHTERM'); - - // Start raid message. - if(strpos($searchterm , 'c0de-') === 0) { - $code_raid_id = explode("-", $searchterm, 2)[1]; - require_once(ROOT_PATH . '/mods/code_start.php'); - exit(); - } +if($new_user && !$access) { + // Tutorial + require_once('tutorial.php'); + exit; +} +// Trim away everything before "/start " +$searchterm = $update['message']['text']; +$searchterm = substr($searchterm, 7); +debug_log($searchterm, 'SEARCHTERM'); - // Get the keys by gym name search. - $keys = ''; - if(!empty($searchterm)) { - $keys = raid_get_gyms_list_keys($searchterm); - } +// Start raid message. +if(strpos($searchterm , 'c0de-') === 0) { + $code_raid_id = explode("-", $searchterm, 2)[1]; + require_once(ROOT_PATH . '/mods/code_start.php'); + exit(); +} - // Get the keys if nothing was returned. - if(!$keys) { - $keys_and_gymarea = raid_edit_gyms_first_letter_keys('raid_by_gym', false, false, 'raid_by_gym_letter'); - $keys = $keys_and_gymarea['keys']; - } +// Get the keys by gym name search. +$addAbortKey = true; +$keys = false; +if(!empty($searchterm)) { + $keys = raid_get_gyms_list_keys($searchterm); + $msg = getTranslation('select_gym_name'); +} - // No keys found. - if (!$keys) { - // Create the keys. - $keys = [ - [ - [ - 'text' => getTranslation('not_supported'), - 'callback_data' => '0:exit:0' - ] - ] - ]; - }else { - $keys[] = [ - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ]; - } - $msg = ''; - // Set message. - if($config->ENABLE_GYM_AREAS) { - if($keys_and_gymarea['gymarea_name'] == '') { - $msg .= '' . getTranslation('select_gym_area') . '' . CR; - }else { - if($keys_and_gymarea['letters']) { - $msg .= '' . getTranslation('select_gym_first_letter_or_gym_area') . '' . CR; - }else { - $msg .= '' . getTranslation('select_gym_name_or_gym_area') . '' . CR; - } - } - }elseif($keys_and_gymarea['letters']) { - $msg .= '' . getTranslation('select_gym_first_letter') . '' . CR; - }else { - $msg .= '' . getTranslation('select_gym_name') . '' . CR; - } - $msg.= (($keys_and_gymarea['gymarea_name'] != '') ? CR . CR . getTranslation('current_gymarea') . ': ' . $keys_and_gymarea['gymarea_name'] : ''); - $msg.= ($config->RAID_VIA_LOCATION ? (CR . CR . getTranslation('send_location')) : ''); +// Get the keys if nothing was returned. +if(!$keys) { + $gymarea = resolveDefaultGymarea($botUser->userId); + $keys_and_gymarea = gymMenu('create', false, 1, false, $gymarea); + $keys = $keys_and_gymarea['keys']; + $msg = $keys_and_gymarea['gymareaTitle']; + $addAbortKey = false; +} - // Send message. - send_message($update['message']['chat']['id'], $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); +// No keys found. +if ($addAbortKey) { + $keys[][] = button(getTranslation('abort'), 'exit'); } -?> + +// Send message. +send_message(create_chat_object([$update['message']['chat']['id']]), $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); diff --git a/commands/team.php b/commands/team.php deleted file mode 100644 index 30489775..00000000 --- a/commands/team.php +++ /dev/null @@ -1,47 +0,0 @@ - 'mystic', - 'instinct' => 'instinct', - 'valor' => 'valor', - getTranslation('red') => 'valor', - getTranslation('blue') => 'mystic', - getTranslation('yellow') => 'instinct', - 'r' => 'valor', - 'b' => 'mystic', - 'y' => 'instinct', - 'g' => 'instinct' -); - -// Valid team name. -if (!empty($gym_team) && $teams[$gym_team]) { - // Update team in raids table. - my_query( - " - UPDATE raids - SET gym_team = '{$teams[$gym_team]}' - WHERE user_id = {$update['message']['from']['id']} - ORDER BY id DESC LIMIT 1 - " - ); - - // Send the message. - send_message($update['message']['chat']['id'], getTranslation('gym_team_set_to') . ' ' . ucfirst($teams[$gym_team])); - -// Invalid team name. -} else { - // Send the message. - send_message($update['message']['chat']['id'], getTranslation('invalid_team')); -} - -?> diff --git a/commands/trainer.php b/commands/trainer.php index f6eeef33..a46237d5 100644 --- a/commands/trainer.php +++ b/commands/trainer.php @@ -1,91 +1,70 @@ accessCheck('trainer'); // Set message. $msg = '' . getTranslation('trainerinfo_set_yours') . ''; -$user_id = $update['message']['from']['id']; -$msg .= CR.CR.get_user($user_id, false); +$user_id = $botUser->userId; +$msg .= CR . CR . get_user($user_id, false); // Init empty keys array. $keys = []; // Create keys array. if($config->CUSTOM_TRAINERNAME){ - $keys[0][] = - [ - 'text' => getTranslation('name'), - 'callback_data' => '0:trainer_name:0' - ]; + $keys[0][] = button(getTranslation('name'), 'trainer_name'); } if($config->RAID_POLL_SHOW_TRAINERCODE){ - $keys[0][] = - [ - 'text' => getTranslation('trainercode'), - 'callback_data' => '0:trainer_code:0' - ]; + $keys[0][] = button(getTranslation('trainercode'), 'trainer_code'); } $keys[] = [ - [ - 'text' => getTranslation('team'), - 'callback_data' => '0:trainer_team:0' - ], - [ - 'text' => getTranslation('level'), - 'callback_data' => '0:trainer_level:0' - ] + button(getTranslation('team'), 'trainer_team'), + button(getTranslation('level'), 'trainer_level') ]; if ($config->RAID_AUTOMATIC_ALARM == false) { - $q_user = my_query("SELECT auto_alarm FROM users WHERE user_id = '{$user_id}' LIMIT 1"); - $alarm_status = $q_user->fetch()['auto_alarm']; - $keys[] = [ - [ - 'text' => ($alarm_status == 1 ? getTranslation('switch_alarm_off') . ' ' . EMOJI_NO_ALARM : getTranslation('switch_alarm_on') . ' ' . EMOJI_ALARM), - 'callback_data' => '0:trainer:a' - ] - ]; + $q_user = my_query('SELECT auto_alarm FROM users WHERE user_id = ? LIMIT 1', [$user_id]); + $alarm_status = $q_user->fetch()['auto_alarm']; + $buttonText = ($alarm_status == 1 ? getTranslation('switch_alarm_off') . ' ' . EMOJI_NO_ALARM : getTranslation('switch_alarm_on') . ' ' . EMOJI_ALARM); + $keys[][] = button($buttonText, ['trainer', 'a' => 1]); } if ($config->LANGUAGE_PRIVATE == '') { - $keys[] = [ - [ - 'text' => getTranslation('bot_lang'), - 'callback_data' => '0:bot_lang:0' - ] - ]; + $keys[][] = button(getTranslation('bot_lang'), 'bot_lang'); +} +if ($config->ENABLE_GYM_AREAS == true) { + $keys[][] = button(getTranslation('default_gymarea'), 'trainerGymarea'); } // Check access. -$access = bot_access_check($update, 'trainer-share', true, true); +$access = $botUser->accessCheck('trainer-share', true); // Display sharing options for admins and users with trainer-share permissions -if($access && (is_file(ROOT_PATH . '/access/' . $access) || $access == 'BOT_ADMINS')) { - // Add sharing keys. - $share_keys = []; - $share_keys[] = universal_inner_key($keys, '0', 'trainer_add', '0', getTranslation('trainer_message_share')); - $share_keys[] = universal_inner_key($keys, '0', 'trainer_delete', '0', getTranslation('trainer_message_delete')); +if($access) { + // Add sharing keys. + $share_keys = []; + $share_keys[] = button(getTranslation('trainer_message_share'), 'trainer_add'); + $share_keys[] = button(getTranslation('trainer_message_delete'), 'trainer_delete'); - // Get the inline key array. - $keys[] = $share_keys; + // Get the inline key array. + $keys[] = $share_keys; - // Add message. - $msg .= CR . CR . getTranslation('trainer_message_share_or_delete'); + // Add message. + $msg .= CR . CR . getTranslation('trainer_message_share_or_delete'); } // Add abort key. $nav_keys = []; -$nav_keys[] = universal_inner_key($keys, '0', 'exit', '0', getTranslation('abort')); +$nav_keys[] = button(getTranslation('done'), 'exit'); // Get the inline key array. $keys[] = $nav_keys; // Send message. -send_message($update['message']['chat']['id'], $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); - -?> +send_message(create_chat_object([$update['message']['chat']['id']]), $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); diff --git a/commands/tutorial.php b/commands/tutorial.php index 3b8b7695..6be95479 100644 --- a/commands/tutorial.php +++ b/commands/tutorial.php @@ -7,26 +7,15 @@ //debug_log($data); // Check access. -bot_access_check($update, 'tutorial'); +$botUser->accessCheck('tutorial'); // Tutorial if(is_file(ROOT_PATH . '/config/tutorial.php')) { - require_once(ROOT_PATH . '/config/tutorial.php'); + require_once(ROOT_PATH . '/config/tutorial.php'); } -$new_user = new_user($update['message']['from']['id']); -if($new_user) { - $msg = $tutorial[0]['msg_new']; -}else { - $msg = $tutorial[0]['msg']; -} -$keys = [ -[ - [ - 'text' => getTranslation("next"), - 'callback_data' => '0:tutorial:1' - ] -] -]; +// New user can already be set if this file was included from start.php. If not, set it here +$new_user = $new_user ?? new_user($update['message']['from']['id']); +$msg = ($new_user) ? $tutorial[0]['msg_new'] : $tutorial[0]['msg']; +$keys[][] = button(getTranslation('next'), ['tutorial', 'p' => 1]); $photo = $tutorial[0]['photo']; -send_photo($update['message']['from']['id'], $photo, $msg, $keys, ['disable_web_page_preview' => 'true'],false); -?> \ No newline at end of file +send_photo(create_chat_object([$update['message']['from']['id']]), $photo, false, $msg, $keys, ['disable_web_page_preview' => 'true'],false); diff --git a/config/config.json.example b/config/config.json.example index 7e2e5ea9..8114a7f4 100644 --- a/config/config.json.example +++ b/config/config.json.example @@ -19,6 +19,5 @@ "RAID_PICTURE": false, "RAID_PICTURE_AUTOEXTEND": false, "RAID_PICTURE_STORE_GYM_IMAGES_LOCALLY": true, - "RAID_PICTURE_URL":"https://example.com/raid/raidpicture.php", "RAID_POLL_UI_TEMPLATE":[["alone","extra","extra_alien","remote","inv_plz","can_inv","ex_inv"],["teamlvl"],["time"],["pokemon"],["refresh","alarm","here","late","done","cancel"]] } diff --git a/config/defaults-config.json b/config/defaults-config.json index 8c7f7b6d..977fbb50 100644 --- a/config/defaults-config.json +++ b/config/defaults-config.json @@ -16,7 +16,7 @@ "APIKEY_HASH":"", "ENABLE_DDOS_PROTECTION": true, "DDOS_MAXIMUM":"10", - "BRIDGE_MODE": false, + "CURL_IPRESOLVE_V4": false, "CURL_USEPROXY": false, "CURL_PROXYSERVER":"http://example.com:8080", "MAINTAINER":"", @@ -30,6 +30,7 @@ "MAPS_LOOKUP": false, "MAPS_API_KEY":"", "OSM_LOOKUP": false, + "OSM_URL": "https://nominatim.openstreetmap.org", "AUTO_REFRESH_POLLS":false, "ENABLE_BOSS_AUTO_UPDATE": true, "CUSTOM_TRAINERNAME": true, @@ -38,14 +39,22 @@ "RAID_VIA_LOCATION": true, "RAID_VIA_LOCATION_FUNCTION": "create", "RAID_EGG_DURATION":"60", + "RAID_EGG_DURATION_ELITE":"1440", + "RAID_BOSS_LIST": false, + "RAID_BOSS_LIST_TITLE": "Raidbosses:", + "RAID_BOSS_LIST_RAID_LEVELS": [5,7,8,10], + "RAID_BOSS_LIST_ROW_LIMIT": "4", "RAID_DURATION":"45", + "RAID_DURATION_ELITE":"30", "RAID_DURATION_CLOCK_STYLE": true, "RAID_CUSTOM_GYM_LETTERS":"", "RAID_EXCLUDE_EXRAID_DUPLICATION": true, + "RAID_EXCLUDE_ELITE_DUPLICATION": true, "RAID_EXCLUDE_EVENT_DUPLICATION": true, "RAID_LOCATION": false, "RAID_SLOTS":"15", "RAID_DIRECT_START": false, + "RAID_RSVP_SLOTS": false, "RAID_FIRST_START":"5", "RAID_LAST_START":"5", "RAID_ANYTIME": false, @@ -67,7 +76,6 @@ "RAID_PICTURE_SHOW_SHINY": true, "RAID_DEFAULT_PICTURE":"images/gym_default.png", "RAID_PICTURE_STORE_GYM_IMAGES_LOCALLY": true, - "RAID_PICTURE_URL":"https://example.com/raid/raidpicture.php", "RAID_PICTURE_FONT_GYM":"NotoSans-Bold.ttf", "RAID_PICTURE_FONT_EX_GYM":"NotoSans-Regular.ttf", "RAID_PICTURE_FONT_TEXT":"NotoSans-Regular.ttf", @@ -94,11 +102,13 @@ "RAID_EX_GYM_MARKER":"icon", "RAID_CREATION_EX_GYM_MARKER": true, "UPGRADE_SQL_AUTO": true, - "TRAINER_MAX_LEVEL":"50", - "TRAINER_BUTTONS_TOGGLE": false, + "TRAINER_MAX_LEVEL":"80", "TRAINER_CHATS":"", "SHARE_CHATS":"", "SHARE_CHATS_LEVEL_X":"", + "SHARE_CHATS_LEVEL_9":"", + "SHARE_CHATS_LEVEL_8":"", + "SHARE_CHATS_LEVEL_7":"", "SHARE_CHATS_LEVEL_6":"", "SHARE_CHATS_LEVEL_5":"", "SHARE_CHATS_LEVEL_4":"", @@ -108,24 +118,32 @@ "WEBHOOK_CREATOR":"bot_or_user_id", "WEBHOOK_CREATE_ONLY": false, "WEBHOOK_CHATS_ALL_LEVELS":"", + "WEBHOOK_CHATS_LEVEL_9":"", + "WEBHOOK_CHATS_LEVEL_8":"", + "WEBHOOK_CHATS_LEVEL_7":"", "WEBHOOK_CHATS_LEVEL_6":"", "WEBHOOK_CHATS_LEVEL_5":"", "WEBHOOK_CHATS_LEVEL_4":"", "WEBHOOK_CHATS_LEVEL_3":"", "WEBHOOK_CHATS_LEVEL_2":"", "WEBHOOK_CHATS_LEVEL_1":"", + "WEBHOOK_CHATS_LEVEL_9_0":"", + "WEBHOOK_CHATS_LEVEL_8_0":"", + "WEBHOOK_CHATS_LEVEL_7_0":"", "WEBHOOK_CHATS_LEVEL_6_0":"", "WEBHOOK_CHATS_LEVEL_5_0":"", "WEBHOOK_CHATS_LEVEL_4_0":"", "WEBHOOK_CHATS_LEVEL_3_0":"", "WEBHOOK_CHATS_LEVEL_2_0":"", "WEBHOOK_CHATS_LEVEL_1_0":"", + "WEBHOOK_CHATS_LEVEL_7_1":"", "WEBHOOK_CHATS_LEVEL_6_1":"", "WEBHOOK_CHATS_LEVEL_5_1":"", "WEBHOOK_CHATS_LEVEL_4_1":"", "WEBHOOK_CHATS_LEVEL_3_1":"", "WEBHOOK_CHATS_LEVEL_2_1":"", "WEBHOOK_CHATS_LEVEL_1_1":"", + "WEBHOOK_CHATS_BY_POKEMON" : [], "WEBHOOK_EXCLUDE_UNKNOWN": false, "WEBHOOK_EXCLUDE_RAID_LEVEL":"1,2,3", "WEBHOOK_EXCLUDE_POKEMON":"", diff --git a/config/defaults-telegram.json b/config/defaults-telegram.json index fa9bd880..7cccb0d7 100644 --- a/config/defaults-telegram.json +++ b/config/defaults-telegram.json @@ -1,4 +1,4 @@ -{ +{ "ALLOWED_TELEGRAM_CONFIG":"RAID_EGG_DURATION,RAID_DURATION,ENABLE_BOSS_AUTO_UPDATE", "ALLOW_ONLY_TRUE_FALSE":"ENABLE_BOSS_AUTO_UPDATE", "ALLOW_ONLY_NUMBERS":"RAID_EGG_DURATION,RAID_DURATION" diff --git a/config/tutorial.php.example b/config/tutorial.php.example index bec2229b..694ec8e0 100644 --- a/config/tutorial.php.example +++ b/config/tutorial.php.example @@ -14,5 +14,3 @@ $tutorial[2]['photo'] = '';// A photo url must be set! $tutorial_done = 'Message that is left to user after completing the tutorial.'; $tutorial_grant_level = 1; // The tutorial value set to user in db (int)0-9 - -?> \ No newline at end of file diff --git a/constants.php b/constants.php index eedf0507..41eafe8c 100644 --- a/constants.php +++ b/constants.php @@ -1,16 +1,114 @@ 1, + '3' => 3, + '4' => 4, + '5' => 5, + '6' => 'MEGA', + '7' => 'MEGA_5', + '8' => 'ULTRA_BEAST', + '9' => 'ELITE', + '10' => 'PRIMAL', + '11' => '1_SHADOW', + '13' => '3_SHADOW', + '15' => '5_SHADOW', + '16' => 'SUPER_MEGA_4', // Pokebattler currently uses MEGA_5 for super mega raids. This needs to be changed if they start using their own tier for super mega raids + '17' => 'SUPER_MEGA_5', // Pokebattler currently uses MEGA_5 for super mega raids. This needs to be changed if they start using their own tier for super mega raids +]; + +$pokebattler_pokemon_map = [ + 'HO_OH' => 'HO-OH', + 'GIRATINA' => 'GIRATINA_ALTERED', + 'ZACIAN' => 'ZACIAN_HERO_FORM', + 'ZAMAZENTA' => 'ZAMAZENTA_HERO_FORM', +]; + +// Storing Primal Kyogre and Groudon for now. These are not needed if Pokebattler starts using own raid tier for primals +define('PRIMAL_MONS', [382, 383]); + +// Limit the tiers of upcoming raid bosses imported from PokeBattler to legendary and mega +$pokebattler_import_future_tiers = [5, 6, 7, 8, 9, 10, 15]; + +define('TYPE_MAP_PROTO_TO_ID', [ + 'POKEMON_TYPE_NORMAL' => 1, + 'POKEMON_TYPE_FIGHTING' => 2, + 'POKEMON_TYPE_FLYING' => 3, + 'POKEMON_TYPE_POISON' => 4, + 'POKEMON_TYPE_GROUND' => 5, + 'POKEMON_TYPE_ROCK' => 6, + 'POKEMON_TYPE_BUG' => 7, + 'POKEMON_TYPE_GHOST' => 8, + 'POKEMON_TYPE_STEEL' => 9, + 'POKEMON_TYPE_FIRE' => 10, + 'POKEMON_TYPE_WATER' => 11, + 'POKEMON_TYPE_GRASS' => 12, + 'POKEMON_TYPE_ELECTRIC' => 13, + 'POKEMON_TYPE_PSYCHIC' => 14, + 'POKEMON_TYPE_ICE' => 15, + 'POKEMON_TYPE_DRAGON' => 16, + 'POKEMON_TYPE_DARK' => 17, + 'POKEMON_TYPE_FAIRY' => 18, +]); + +// Default language. +defined('DEFAULT_LANGUAGE') or define('DEFAULT_LANGUAGE', 'EN'); + +// Telegram language code => Language files. +$languages = array( + 'nl' => 'NL', + 'de' => 'DE', + 'en' => 'EN', + 'it' => 'IT', + 'pt' => 'PT-BR', + 'ru' => 'RU', + 'fr' => 'FR', + 'fi' => 'FI', + 'es' => 'ES', +); // Value used for denoting anytime attendance define('ANYTIME', '1970-01-01 00:00:00'); -define('ANYTIME_TS', preg_replace("/[^0-9]/", "", ANYTIME)); - -// Ex-raid event ID -defined('EVENT_ID_EX') or define('EVENT_ID_EX', '999'); +define('ANYTIME_TS', preg_replace('/[^0-9]/', '', ANYTIME)); // Icons. defined('TEAM_B') or define('TEAM_B', iconv('UCS-4LE', 'UTF-8', pack('V', 0x1F499))); @@ -28,6 +126,9 @@ defined('EMOJI_STAR') or define('EMOJI_STAR', iconv('UCS-4LE', 'UTF-8', pack('V', 0x2B50))); defined('EMOJI_INVITE') or define('EMOJI_INVITE', iconv('UCS-4LE', 'UTF-8', pack('V', 0x2709))); defined('EMOJI_INFO') or define('EMOJI_INFO', iconv('UCS-4LE', 'UTF-8', pack('V', 0x2139))); +defined('EMOJI_DELETE') or define('EMOJI_DELETE', iconv('UCS-4LE', 'UTF-8', pack('V', 0x274C))); +defined('EMOJI_MAP') or define('EMOJI_MAP', iconv('UCS-4LE', 'UTF-8', pack('V', 0x1F5FA))); +defined('EMOJI_PENCIL') or define('EMOJI_PENCIL', iconv('UCS-4LE', 'UTF-8', pack('V', 0x270F))); defined('EMOJI_EGG') or define('EMOJI_EGG', iconv('UCS-4LE', 'UTF-8', pack('V', 0x1F95A))); defined('EMOJI_CLOCK') or define('EMOJI_CLOCK', iconv('UCS-4LE', 'UTF-8', pack('V', 0x1F551))); defined('EMOJI_CAMERA') or define('EMOJI_CAMERA', iconv('UCS-4LE', 'UTF-8', pack('V', 0x1F4F7))); @@ -38,6 +139,7 @@ defined('EMOJI_IN_PERSON') or define('EMOJI_IN_PERSON', iconv('UCS-4LE', 'UTF-8', pack('V', 0x1F9E1))); defined('EMOJI_ALIEN') or define('EMOJI_ALIEN', iconv('UCS-4LE', 'UTF-8', pack('V', 0x1F47D))); defined('EMOJI_CAN_INVITE')or define('EMOJI_CAN_INVITE', iconv('UCS-4LE', 'UTF-8', pack('V', 0x1F695))); +defined('EMOJI_SHINY') or define('EMOJI_SHINY', iconv('UCS-4LE', 'UTF-8', pack('V', 0x2728))); // Weather Icons. defined('EMOJI_W_SUNNY') or define('EMOJI_W_SUNNY', iconv('UCS-4LE', 'UTF-8', pack('V', 0x2600))); @@ -49,33 +151,35 @@ defined('EMOJI_W_SNOW') or define('EMOJI_W_SNOW', iconv('UCS-4LE', 'UTF-8', pack('V', 0x2744))); defined('EMOJI_W_FOG') or define('EMOJI_W_FOG', iconv('UCS-4LE', 'UTF-8', pack('V', 0x1F301))); +defined('EMOJI_WARN') or define('EMOJI_WARN', iconv('UCS-4LE', 'UTF-8', pack('V', 0x26A0))); +defined('EMOJI_DISK') or define('EMOJI_DISK', iconv('UCS-4LE', 'UTF-8', pack('V', 0x1F4BE))); +defined('EMOJI_NEW') or define('EMOJI_NEW', iconv('UCS-4LE', 'UTF-8', pack('V', 0x1F195))); +defined('EMOJI_CLIPPY') or define('EMOJI_CLIPPY',iconv('UCS-4LE', 'UTF-8', pack('V', 0x1F4CE))); +defined('EMOJI_DISABLED') or define('EMOJI_DISABLED',iconv('UCS-4LE', 'UTF-8', pack('V', 0x1F645))); + +// Carriage return. +defined('CR') or define('CR', "\n"); +defined('CR2') or define('CR2', "\n"); +// Space. +defined('SP') or define('SP', ' '); + // Weather. $weather = array( - '1' => EMOJI_W_SUNNY, - '2' => EMOJI_W_CLEAR, - '3' => EMOJI_W_RAIN, - '4' => EMOJI_W_PARTLY_CLOUDY, - '5' => EMOJI_W_CLOUDY, - '6' => EMOJI_W_WINDY, - '7' => EMOJI_W_SNOW, - '8' => EMOJI_W_FOG + '1' => EMOJI_W_SUNNY, + '2' => EMOJI_W_CLEAR, + '3' => EMOJI_W_RAIN, + '4' => EMOJI_W_PARTLY_CLOUDY, + '5' => EMOJI_W_CLOUDY, + '6' => EMOJI_W_WINDY, + '7' => EMOJI_W_SNOW, + '8' => EMOJI_W_FOG ); // Teams. $teams = array( - 'mystic' => TEAM_B, - 'valor' => TEAM_R, - 'instinct' => TEAM_Y, - 'unknown' => TEAM_UNKNOWN, - 'cancel' => TEAM_CANCEL -); - -// Raid eggs. -$eggs = array( - '9996', // Level 6 / Mega - '9995', // Level 5 - '9994', // Level 4 - '9993', // Level 3 - '9992', // Level 2 - '9991' // Level 1 + 'mystic' => TEAM_B, + 'valor' => TEAM_R, + 'instinct' => TEAM_Y, + 'unknown' => TEAM_UNKNOWN, + 'cancel' => TEAM_CANCEL ); diff --git a/core/bot/apikey.php b/core/bot/apikey.php index 5f20335d..e2b845cd 100644 --- a/core/bot/apikey.php +++ b/core/bot/apikey.php @@ -2,32 +2,27 @@ /** * Verify a GET param apikey and return a provided update Array - * @return Array + * @return array */ function get_verified_update(){ - global $config; + global $config, $argv; // Get api key from get parameters. - if(isset($_GET['apikey'])) { - $apiKey = $_GET['apikey']; - // Get api key from argv. - } elseif(!empty($argv[1])) { - $apiKey = $argv[1]; - } else { - debug_log('Called without apikey, returning empty content.'); - http_response_code(204); // HTTP 204: No Content - exit(); + if(!isset($_GET['apikey'])) { + debug_log('Called without apikey, returning empty content.'); + http_response_code(204); // HTTP 204: No Content + exit(); } + $apiKey = $_GET['apikey']; // Check if hashed api key is matching config. - if (hash('sha512', $apiKey) == strtolower($config->APIKEY_HASH)) { - // Set constants. - define('API_KEY', $apiKey); - } else { - info_log('Incorrect apikey provided! This is most likely a misconfiguration you should fix.'); - http_response_code(403); // HTTP 403: Forbidden - exit(); + if (hash('sha512', $apiKey) != strtolower($config->APIKEY_HASH)) { + info_log('Incorrect apikey provided! This is most likely a misconfiguration you should fix.'); + http_response_code(403); // HTTP 403: Forbidden + exit(); } + // Set constants. + define('API_KEY', $apiKey); // Get content from POST data. $update = null; @@ -36,12 +31,6 @@ function get_verified_update(){ if($content) { // Decode the json string. $update = json_decode($content, true); - } elseif(!empty($argv[2])) { - $arg_content = addslashes($argv[2]); - $update = json_decode($argv[2], true); - } - - if ($update) { debug_log_incoming($update, '<'); } diff --git a/core/bot/cleanup_collect.php b/core/bot/cleanup_collect.php index f416f5ab..68364e53 100644 --- a/core/bot/cleanup_collect.php +++ b/core/bot/cleanup_collect.php @@ -6,30 +6,42 @@ // Init ID. $cleanup_id = 0; $message = null; -// Channel +// Channel if(isset($update['channel_post']['text'])) { - $message = $update['channel_post']; + $message = $update['channel_post']; // Supergroup } else if (isset($update['message']['text']) && ($update['message']['chat']['type'] == "supergroup" || $update['message']['chat']['type'] == "group")) { - $message = $update['message']; + $message = $update['message']; } if($message != null) { - // Get chat_id and message_id - $chat_id = $message['chat']['id']; - $message_id = $message['message_id']; - if(isset($message['reply_markup']['inline_keyboard'])) { - $split_data = explode(':', $message['reply_markup']['inline_keyboard'][0][0]['callback_data']); - $cleanup_id = $split_data[0]; - }else { - // Get id from text. - $cleanup_id = substr($message['text'],strpos($message['text'], substr(strtoupper($config->BOT_ID), 0, 1) . '-ID = ') + 7); + // Get chat_id and message_id + $chat_id = $message['chat']['id']; + $message_id = $message['message_id']; + $thread_id = $message['message_thread_id'] ?? NULL; + if(isset($message['reply_markup']['inline_keyboard'])) { + $splitData = explode('|', $message['reply_markup']['inline_keyboard'][0][0]['callback_data']); + // Search for raid id in the first button of the message + for($i=1;$iBOT_ID), 0, 1) . '-ID = ') + 7); + if(preg_match("^[0-9]+^", $idFromText)) { + $cleanup_id = $idFromText; + } + } - // Write cleanup info to database. + // Write cleanup info to database. + if($cleanup_id != 0) { cleanup_log('Calling cleanup preparation now!'); cleanup_log('Cleanup_ID: ' . $cleanup_id); - if($cleanup_id != 0) { - insert_cleanup($chat_id, $message_id, $cleanup_id, 'inline_poll_text'); - } + require_once(LOGIC_PATH . '/insert_cleanup.php'); + insert_cleanup($chat_id, $message_id, $thread_id, $cleanup_id, 'inline_poll_text'); + } } diff --git a/core/bot/cleanup_run.php b/core/bot/cleanup_run.php index fb9598b6..5df8138b 100644 --- a/core/bot/cleanup_run.php +++ b/core/bot/cleanup_run.php @@ -5,16 +5,15 @@ function cleanup_auth_and_run($update){ global $config; cleanup_log('Cleanup request received.'); - if ($update['cleanup']['secret'] == $config->CLEANUP_SECRET) { - cleanup_log('Cleanup is authenticated.'); - perform_cleanup(); - } else { - $error = 'Cleanup authentication failed.'; - http_response_code(403); - error_log($error); - die($error); - } - exit(); + if ($update['cleanup']['secret'] != $config->CLEANUP_SECRET) { + $error = 'Cleanup authentication failed.'; + http_response_code(403); + error_log($error); + die($error); + } + cleanup_log('Cleanup is authenticated.'); + perform_cleanup(); + exit(); } // Do the actual cleanup. Authentication or authorization is not considered but config is checked @@ -39,68 +38,70 @@ function perform_cleanup(){ throw new Exception($error); } // Start cleanup when at least one parameter is set to trigger cleanup - if ($config->CLEANUP_TELEGRAM || $config->CLEANUP_DATABASE) { - // Query for telegram cleanup without database cleanup - if ($config->CLEANUP_TELEGRAM) { - // Get cleanup info for telegram cleanup. - $rs = my_query(' - SELECT cleanup.id, cleanup.raid_id, cleanup.chat_id, cleanup.message_id, raids.gym_id, - IF(date_of_posting < DATE_SUB(UTC_TIMESTAMP(), INTERVAL 48 HOUR), 1, 0) as skip_del_message - FROM cleanup - LEFT JOIN raids - ON cleanup.raid_id = raids.id - WHERE raids.end_time < DATE_SUB(UTC_TIMESTAMP(), INTERVAL '.$config->CLEANUP_TIME_TG.' MINUTE) - '); - $cleanup_ids = []; - cleanup_log('Telegram cleanup starting. Found ' . $rs->rowCount() . ' entries for cleanup.'); - if($rs->rowCount() > 0) { - while($row = $rs->fetch()) { - if($row['skip_del_message'] == 0) { - delete_message($row['chat_id'], $row['message_id']); - cleanup_log('Deleting raid: '.$row['raid_id'].' from chat '.$row['chat_id'].' (message_id: '.$row['message_id'].')'); - if ($metrics){ - $cleanup_total->inc(['telegram']); - } - } else { - cleanup_log('Chat message for raid '.$row['raid_id'].' in chat '.$row['chat_id'].' is over 48 hours old. It can\'t be deleted by the bot. Skipping deletion and removing database entry.'); - } - $cleanup_ids[] = $row['id']; - } - my_query('DELETE FROM cleanup WHERE id IN (' . implode(',', $cleanup_ids) . ')'); - } + if (!$config->CLEANUP_TELEGRAM && !$config->CLEANUP_DATABASE) { + cleanup_log('Cleanup was called, but nothing was done. Check your config and cleanup request for which actions you would like to perform (Telegram and/or database cleanup)'); + return; + } + // Query for telegram cleanup without database cleanup + if ($config->CLEANUP_TELEGRAM) { + // Get cleanup info for telegram cleanup. + $rs = my_query(' + SELECT cleanup.id, cleanup.raid_id, cleanup.chat_id, cleanup.message_id, raids.gym_id, + IF(date_of_posting < DATE_SUB(UTC_TIMESTAMP(), INTERVAL 48 HOUR), 1, 0) as skip_del_message + FROM cleanup + LEFT JOIN raids + ON cleanup.raid_id = raids.id + WHERE raids.end_time < DATE_SUB(UTC_TIMESTAMP(), INTERVAL '.$config->CLEANUP_TIME_TG.' MINUTE) + '); + $cleanup_ids = []; + cleanup_log('Telegram cleanup starting. Found ' . $rs->rowCount() . ' entries for cleanup.'); + if($rs->rowCount() > 0) { + while($row = $rs->fetch()) { + if($row['skip_del_message'] == 1) { + cleanup_log('Chat message for raid '.$row['raid_id'].' in chat '.$row['chat_id'].' is over 48 hours old. It can\'t be deleted by the bot. Skipping deletion and removing database entry.'); + continue; } - if($config->CLEANUP_DATABASE) { - cleanup_log('Database cleanup called.'); - $rs_temp_gyms = my_query(' - SELECT gyms.id, gyms.gym_name - FROM gyms - LEFT JOIN raids - ON raids.gym_id = gyms.id - WHERE (raids.end_time < DATE_SUB(UTC_TIMESTAMP(), INTERVAL '.$config->CLEANUP_TIME_DB.' MINUTE)) - AND temporary_gym = 1 - '); - if($rs_temp_gyms->rowCount() > 0) { - $cleanup_gyms = []; - while($row = $rs_temp_gyms->fetch()) { - $cleanup_gyms[] = $row['id']; - cleanup_log('Deleting temporary gym ' . $row['id'] . ' from database.'); - } - if(count($cleanup_gyms) > 0) { - my_query('DELETE FROM gyms WHERE id IN (' . implode(',', $cleanup_gyms) . ')'); - } - } - $q_a = my_query('DELETE FROM attendance WHERE raid_id IN (SELECT id FROM raids WHERE raids.end_time < DATE_SUB(UTC_TIMESTAMP(), INTERVAL '.$config->CLEANUP_TIME_DB.' MINUTE))'); - $q_r = my_query('DELETE FROM raids WHERE end_time < DATE_SUB(UTC_TIMESTAMP(), INTERVAL '.$config->CLEANUP_TIME_DB.' MINUTE)'); - cleanup_log('Cleaned ' . $q_a->rowCount() . ' rows from attendance table'); - cleanup_log('Cleaned ' . $q_r->rowCount() . ' rows from raids table'); - if ($metrics){ - $cleanup_total->incBy($q_a->rowCount(), ['db_attendance']); - $cleanup_total->incBy($q_r->rowCount(), ['db_raids']); - } + if(delete_message($row['chat_id'], $row['message_id']) === false) continue; + $cleanup_ids[] = $row['id']; + cleanup_log('Deleting raid: '.$row['raid_id'].' from chat '.$row['chat_id'].' (message_id: '.$row['message_id'].')'); + if ($metrics){ + $cleanup_total->inc(['telegram']); } - // Write to log. - cleanup_log('Finished with cleanup process!'); - }else { - cleanup_log('Cleanup was called, but nothing was done. Check your config and cleanup request for which actions you would like to perform (Telegram and/or database cleanup)'); + } + my_query('DELETE FROM cleanup WHERE id IN (' . implode(',', $cleanup_ids) . ')'); } + } + if($config->CLEANUP_DATABASE) { + cleanup_log('Database cleanup called.'); + $rs_temp_gyms = my_query(' + SELECT gyms.id, gyms.gym_name + FROM gyms + LEFT JOIN raids + ON raids.gym_id = gyms.id + WHERE (raids.end_time < DATE_SUB(UTC_TIMESTAMP(), INTERVAL '.$config->CLEANUP_TIME_DB.' MINUTE)) + AND temporary_gym = 1 + '); + if($rs_temp_gyms->rowCount() > 0) { + $cleanup_gyms = []; + while($row = $rs_temp_gyms->fetch()) { + $cleanup_gyms[] = $row['id']; + cleanup_log('Deleting temporary gym ' . $row['id'] . ' from database.'); + } + if(count($cleanup_gyms) > 0) { + my_query('DELETE FROM gyms WHERE id IN (' . implode(',', $cleanup_gyms) . ')'); + } + } + $q_a = my_query('DELETE FROM attendance WHERE raid_id IN (SELECT id FROM raids WHERE raids.end_time < DATE_SUB(UTC_TIMESTAMP(), INTERVAL '.$config->CLEANUP_TIME_DB.' MINUTE))'); + $q_r = my_query('DELETE FROM raids WHERE end_time < DATE_SUB(UTC_TIMESTAMP(), INTERVAL '.$config->CLEANUP_TIME_DB.' MINUTE)'); + $q_p = my_query('DELETE photo_cache FROM photo_cache LEFT JOIN raids ON photo_cache.raid_id = raids.id WHERE photo_cache.ended = 0 AND raids.id IS NULL'); + cleanup_log('Cleaned ' . $q_a->rowCount() . ' rows from attendance table'); + cleanup_log('Cleaned ' . $q_r->rowCount() . ' rows from raids table'); + cleanup_log('Cleaned ' . $q_p->rowCount() . ' rows from photo_cache table'); + if ($metrics){ + $cleanup_total->incBy($q_a->rowCount(), ['db_attendance']); + $cleanup_total->incBy($q_r->rowCount(), ['db_raids']); + } + } + // Write to log. + cleanup_log('Finished with cleanup process!'); } diff --git a/core/bot/commands.php b/core/bot/commands.php index ecc6d8c0..d3219f3b 100644 --- a/core/bot/commands.php +++ b/core/bot/commands.php @@ -4,54 +4,35 @@ // Check message text for a leading slash. if (isset($update['message']['text']) && substr($update['message']['text'], 0, 1) == '/') { - // Get command name. - if($config->BOT_NAME) { - $com = strtolower(str_replace('/', '', str_replace($config->BOT_NAME, '', explode(' ', $update['message']['text'])[0]))); - $altcom = strtolower(str_replace('/' . basename(ROOT_PATH), '', str_replace($config->BOT_NAME, '', explode(' ', $update['message']['text'])[0]))); - } else { - info_log('BOT_NAME is missing! Please define it!', '!'); - $com = 'start'; - $altcom = 'start'; - } + // Get command name. + if($config->BOT_NAME) { + $com = strtolower(str_replace('/', '', str_replace($config->BOT_NAME, '', explode(' ', $update['message']['text'])[0]))); + } else { + info_log('BOT_NAME is missing! Please define it!', '!'); + $com = 'start'; + } - if($config->TUTORIAL_MODE && isset($update['message']['chat']['id']) && new_user($update['message']['chat']['id']) && $com != 'start' && $com != 'tutorial') { - send_message($update['message']['chat']['id'], getTranslation("tutorial_command_failed")); - $dbh = null; - exit(); - } + if(isset($update['message']['chat']['id']) && new_user($update['message']['chat']['id']) && $com != 'start' && $com != 'tutorial') { + send_message(create_chat_object([$update['message']['chat']['id']]), getTranslation("tutorial_command_failed")); + exit(); + } - // Set command paths. - $command = ROOT_PATH . '/commands/' . basename($com) . '.php'; - $altcommand = ROOT_PATH . '/commands/' . basename($altcom) . '.php'; - $core_command = CORE_COMMANDS_PATH . '/' . basename($com) . '.php'; - $core_altcommand = CORE_COMMANDS_PATH . '/' . basename($altcom) . '.php'; - $startcommand = ROOT_PATH . '/commands/start.php'; + // Set command paths. + $command = ROOT_PATH . '/commands/' . basename($com) . '.php'; + $startcommand = ROOT_PATH . '/commands/start.php'; - // Write to log. - debug_log(CORE_PATH,'Core path'); - debug_log('Command-File: ' . $command); - debug_log('Alternative Command-File: ' . $altcommand); - debug_log('Core Command-File: ' . $core_command); - debug_log('Core Alternative Command-File: ' . $core_altcommand); - debug_log('Start Command-File: ' . $startcommand); + // Write to log. + debug_log('Command-File: ' . $command); + debug_log('Start Command-File: ' . $startcommand); - // Check if command file exits. - if (is_file($command)) { - // Dynamically include command file and exit. - include_once($command); - } else if (is_file($altcommand)) { - // Dynamically include command file and exit. - include_once($altcommand); - } else if (is_file($core_command)) { - // Dynamically include command file and exit. - include_once($core_command); - } else if (is_file($core_altcommand)) { - // Dynamically include command file and exit. - include_once($core_altcommand); - } else if ($com == basename(ROOT_PATH)) { - // Include start file and exit. - include_once($startcommand); - } else { - send_message($update['message']['chat']['id'], '' . getTranslation('not_supported') . ''); - } + // Check if command file exits. + if (is_file($command)) { + // Dynamically include command file and exit. + include_once($command); + } else if ($com == basename(ROOT_PATH)) { + // Include start file and exit. + include_once($startcommand); + } else { + send_message(create_chat_object([$update['message']['chat']['id']]), '' . getTranslation('not_supported') . ''); + } } diff --git a/core/bot/config.php b/core/bot/config.php index ecbfc1d6..b4365a97 100644 --- a/core/bot/config.php +++ b/core/bot/config.php @@ -12,11 +12,11 @@ function get_config_array($file) { error_log('Unable to read config file, check permissions: ' . $file); die('Config file not readable, cannot continue: ' . $file); } - + $config_array = json_decode($file_contents, true); if(json_last_error() !== JSON_ERROR_NONE) { - error_log('Invalid JSON (' . json_last_error_msg() . '): ' . $file); + error_log('[core/bot/config.php] Invalid JSON (' . json_last_error_msg() . '): ' . $file . "\n What we got from the file: \n" . $file_contents); die('Config file not valid JSON, cannot continue: ' . $file); } @@ -58,6 +58,9 @@ function migrate_config($config, $file){ function build_config() { // Get default config files without their full path, e.g. 'defaults-config.json' $default_configs = str_replace(CONFIG_PATH . '/', '', glob(CONFIG_PATH . '/defaults-*.json')); + if(!$default_configs){ + error_log('No config defaults found, make sure you have not destroyed config/defaults-config.json. Things will break unless your custom config sets every possible option.'); + } // Collection point for individual configfile arrays, will eventually be converted to a json object $config = Array(); @@ -65,7 +68,7 @@ function build_config() { // Iterate over subconfigs getting defaults and merging in custom overrides foreach ($default_configs as $filename) { $dfile = CONFIG_PATH . '/' . $filename; // config defaults, e.g. defaults-config.json - $cfile = CONFIG_PATH . '/' . str_replace('defaults-', '', $filename); // custom config overrides e.g. config.json + $cfile = botSpecificConfigFile(str_replace('defaults-', '', $filename)); // custom config overrides e.g. config.json // Get default config as an array so we can do an array merge later $config_array = get_config_array($dfile); @@ -86,8 +89,12 @@ function build_config() { // Return the whole multi-source config as an Object return (Object)$config; } +function botSpecificConfigFile($filename) { + $prefix = ''; + if (isset($_GET['bot_name']) && !empty($_GET['bot_name'])) + $prefix = $_GET['bot_name'] . '-'; + return CONFIG_PATH . '/' . $prefix . $filename; +} // Object, access a config option with e.g. $config->VERSION $config = build_config(); - -?> diff --git a/core/bot/constants.php b/core/bot/constants.php deleted file mode 100644 index 9d5e3101..00000000 --- a/core/bot/constants.php +++ /dev/null @@ -1,30 +0,0 @@ - Language files. -$languages = array( - 'nl' => 'NL', - 'de' => 'DE', - 'en-US' => 'EN', - 'it' => 'IT', - 'pt' => 'PT-BR', - 'ru' => 'RU', - 'fr' => 'FR', - 'fi' => 'FI', - 'es' => 'ES', -); - -// Icons. -defined('EMOJI_WARN') or define('EMOJI_WARN', iconv('UCS-4LE', 'UTF-8', pack('V', 0x26A0))); -defined('EMOJI_DISK') or define('EMOJI_DISK', iconv('UCS-4LE', 'UTF-8', pack('V', 0x1F4BE))); -defined('EMOJI_NEW') or define('EMOJI_NEW', iconv('UCS-4LE', 'UTF-8', pack('V', 0x1F195))); -defined('EMOJI_CLIPPY') or define('EMOJI_CLIPPY', iconv('UCS-4LE', 'UTF-8', pack('V', 0x1F4CE))); - diff --git a/core/bot/db.php b/core/bot/db.php index c1b0db46..bf9a1330 100644 --- a/core/bot/db.php +++ b/core/bot/db.php @@ -1,40 +1,38 @@ DB_HOST && $config->DB_PORT && $config->DB_NAME && $config->DB_USER && $config->DB_PASSWORD) { - // Establish PDO connection - $dbh = new PDO( - "mysql:host=" . $config->DB_HOST - . ";port=" . $config->DB_PORT - . ";dbname=" . $config->DB_NAME - . ";charset=utf8mb4", - $config->DB_USER, - $config->DB_PASSWORD, - array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION) - ); - $dbh->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_EMPTY_STRING); - $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); - // Route SQL errors to Exceptions so we can handle them centrally - $dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - - // Verify connection works and the DB schema has been loaded - $query = $dbh->prepare('SHOW TABLES LIKE "attendance";'); - $query->execute(); - $table_exists = $query->fetch(PDO::FETCH_NUM); - if(empty($table_exists)){ - info_log('Database has not been initialized yet, running it now!'); - $success = run_sql_file(ROOT_PATH . '/sql/pokemon-raid-bot.sql'); - if(!$success){ - $error = "DB initialization failed, see error_log for more details."; - info_log($error); - die($error); - } else { - info_log('DB init success!'); - } - } -} else { +if(!$config->DB_HOST || !$config->DB_PORT || !$config->DB_NAME || !$config->DB_USER || !$config->DB_PASSWORD) { $error = "Failed to connect to Database! Make sure DB_HOST, DB_NAME, DB_USER and DB_PASSWORD are defined and that you've provided a config/config.json in the first place!"; error_log($error); http_response_code(409); die($error); } +// Establish PDO connection +$dbh = new PDO( + "mysql:host=" . $config->DB_HOST + . ";port=" . $config->DB_PORT + . ";dbname=" . $config->DB_NAME + . ";charset=utf8mb4", + $config->DB_USER, + $config->DB_PASSWORD, + array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION) +); +$dbh->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_EMPTY_STRING); +$dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); +// Route SQL errors to Exceptions so we can handle them centrally +$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +// Verify connection works and the DB schema has been loaded +$query = $dbh->prepare('SHOW TABLES LIKE "attendance";'); +$query->execute(); +$table_exists = $query->fetch(PDO::FETCH_NUM); +if(empty($table_exists)){ + info_log('Database has not been initialized yet, running it now!'); + $success = run_sql_file(ROOT_PATH . '/sql/pokemon-raid-bot.sql'); + if(!$success){ + $error = "DB initialization failed, see error_log for more details."; + info_log($error); + die($error); + } + info_log('DB init success!'); +} diff --git a/core/bot/ddos.php b/core/bot/ddos.php index a39e6a21..d84b2df8 100644 --- a/core/bot/ddos.php +++ b/core/bot/ddos.php @@ -2,119 +2,98 @@ // Write to log debug_log('DDOS Check'); if ($metrics){ - $ddos_old_updates_total = $metrics->registerCounter($namespace, 'ddos_old_updates_total', 'Total old updates received'); - $ddos_last_update = $metrics->registerGauge($namespace, 'ddos_last_update', 'Last known update_id'); - $ddos_state = $metrics->registerGauge($namespace, 'ddos_state', 'current DDoS values', ['user_id']); - $ddos_fails_total = $metrics->registerCounter($namespace, 'ddos_fails_total', 'Total DDoS failures', ['user_id']); + $ddos_old_updates_total = $metrics->registerCounter($namespace, 'ddos_old_updates_total', 'Total old updates received'); + $ddos_last_update = $metrics->registerGauge($namespace, 'ddos_last_update', 'Last known update_id'); + $ddos_state = $metrics->registerGauge($namespace, 'ddos_state', 'current DDoS values', ['user_id']); + $ddos_fails_total = $metrics->registerCounter($namespace, 'ddos_fails_total', 'Total DDoS failures', ['user_id']); } -// Update_ID file. -$id_file = DDOS_PATH . '/update_id'; +verifyUpdate($update, $data); +ddosCheck($update, $data); -// Skip DDOS check for specific stuff, e.g. cleanup and overview refresh. -$skip_ddos_check = 0; +function verifyUpdate($update, $data) { + global $metrics; + if ($update['type'] == 'callback_query' + && (in_array($data[0], ['overview_refresh', 'refresh_polls', 'getdb', 'update_bosses']) + or ($data[0] == 'post_raid' && $update['skip_ddos'] == true)) + or isset($update['cleanup'])) + { + debug_log('Skipping DDOS check...','!'); + return; + } + // Prepend update_id with bot name if it's set + $id_file = DDOS_PATH . '/' . (isset($_GET['bot_name']) && !empty($_GET['bot_name']) ? $_GET['bot_name'] . '-' : ''). 'update_id'; -// Update the update_id and reject old updates -if (file_exists($id_file) && filesize($id_file) > 0) { - // Get update_ids from Telegram and locally stored in the file - $update_id = isset($update['update_id']) ? $update['update_id'] : 0; - $last_update_id = is_file($id_file) ? file_get_contents($id_file) : 0; - if ($metrics){ - $ddos_last_update->set($last_update_id); - } - if (isset($update['callback_query'])) { - $action = $data['action']; - if ($action == 'overview_refresh') { - $skip_ddos_check = 1; - debug_log('Skipping DDOS check for overview refresh...','!'); - }else if ($action == 'refresh_polls') { - $skip_ddos_check = 1; - debug_log('Skipping DDOS check for poll refresh...','!'); - }else if ($action == 'post_raid' && $update['skip_ddos'] == true) { - $skip_ddos_check = 1; - debug_log('Skipping DDOS check for posting raid directly...','!'); - }else if ($action == 'getdb') { - $skip_ddos_check = 1; - debug_log('Skipping DDOS check for database update...','!'); - }else if ($action == 'update_bosses') { - $skip_ddos_check = 1; - debug_log('Skipping DDOS check for boss data update...','!'); - } - } else if(isset($update['cleanup'])) { - $skip_ddos_check = 1; - debug_log('Skipping DDOS check for cleanup...','!'); - } + // Update the update_id and reject old updates + // Get update_ids from Telegram and locally stored in the file + $update_id = isset($update['update_id']) ? $update['update_id'] : 0; + $last_update_id = is_file($id_file) ? file_get_contents($id_file) : 0; + if ($metrics){ + $GLOBALS['ddos_last_update']->set($last_update_id); + } - // End script if update_id is older than stored update_id - if ($update_id < $last_update_id && $skip_ddos_check == 0) { - info_log("FATAL ERROR! Received old update_id: {$update_id} vs {$last_update_id}",'!'); - if ($metrics){ - $ddos_old_updates_total->incBy(1); - } - exit(); + // End script if update_id is older than stored update_id + if ($update_id < $last_update_id) { + info_log("FATAL ERROR! Received old update_id: {$update_id} vs {$last_update_id}",'!'); + if ($metrics){ + $GLOBALS['ddos_old_updates_total']->incBy(1); } -} else { - // Create file with initial update_id - $update_id = 1; + exit(); + } + // Write update_id to file + file_put_contents($id_file, $update_id); } -// Write update_id to file -if($skip_ddos_check == 0) { - file_put_contents($id_file, $update_id); -} - -// Init DDOS count -$ddos_count = 0; +function ddosCheck($update, $data) { + global $botUser, $metrics, $config; + // DDOS protection + // Init DDOS count + $ddos_count = 0; + // Get callback query data + if (!isset($update['callback_query']) or !$update['callback_query']['data']) return; + // Split callback data and assign to data array. + $splitAction = explode('_', $data[0]); + // Check the action + if ($splitAction[0] != 'vote') return; -// DDOS protection -if (isset($update['callback_query'])) { - // Get callback query data - if ($update['callback_query']['data']) { - // Split callback data and assign to data array. - $splitAction = explode('_', $data['action']); - // Check the action - if ($splitAction[0] == 'vote') { - // Get the user_id and set the related ddos file - $ddos_id = $update['callback_query']['from']['id']; - $ddos_file = (DDOS_PATH . '/' . $ddos_id); - // Check if ddos file exists and is not empty - if (file_exists($ddos_file) && filesize($ddos_file) > 0) { - // Get current time and last modification time of file - $now = date("YmdHi"); - $lastchange = date("YmdHi", filemtime($ddos_file)); - // Get DDOS count or rest DDOS count if new minute - if ($now == $lastchange) { - // Get DDOS count from file - $ddos_count = file_get_contents($ddos_file); - $ddos_count = $ddos_count + 1; - if ($metrics){ - $ddos_state->set($ddos_count, [$ddos_id]); - } - // Reset DDOS count to 1 - } else { - $ddos_count = 1; - if ($metrics){ - $ddos_state->set(1, [$ddos_id]); - } - } - // Exit if DDOS of user_id count is exceeded. - if ($ddos_count > $config->DDOS_MAXIMUM) { - if ($metrics){ - $ddos_fails_total->inc([$ddos_id]); - } - exit(); - // Update DDOS count in file - } else { - file_put_contents($ddos_file, $ddos_count); - } - // Create file with initial DDOS count - } else { - $ddos_count = 1; - file_put_contents($ddos_file, $ddos_count); - if ($metrics){ - $ddos_state->set($ddos_count, [$ddos_id]); - } - } - } + // Get the user_id and set the related ddos file + $ddos_id = $update['callback_query']['from']['id']; + $ddos_file = (DDOS_PATH . '/' . $ddos_id); + // Check if ddos file exists and is not empty + if (!file_exists($ddos_file) || filesize($ddos_file) == 0) { + // Create file with initial DDOS count + $ddos_count = 1; + file_put_contents($ddos_file, $ddos_count); + if ($metrics){ + $GLOBALS['ddos_state']->set($ddos_count, [$ddos_id]); + } + return; + } + // Get current time and last modification time of file + $now = date("YmdHi"); + $lastchange = date("YmdHi", filemtime($ddos_file)); + // Get DDOS count or rest DDOS count if new minute + if ($now == $lastchange) { + // Get DDOS count from file + $ddos_count = file_get_contents($ddos_file); + $ddos_count = $ddos_count + 1; + if ($metrics){ + $GLOBALS['ddos_state']->set($ddos_count, [$ddos_id]); + } + // Reset DDOS count to 1 + } else { + $ddos_count = 1; + if ($metrics){ + $GLOBALS['ddos_state']->set(1, [$ddos_id]); + } + } + // Exit if DDOS of user_id count is exceeded. + if ($ddos_count > $config->DDOS_MAXIMUM) { + if ($metrics){ + $GLOBALS['ddos_fails_total']->inc([$ddos_id]); } + exit(); + } + file_put_contents($ddos_file, $ddos_count); + $botUser->ddosCount = $ddos_count; } diff --git a/core/bot/error_handlers.php b/core/bot/error_handlers.php index 63b0bce1..f6942fdb 100644 --- a/core/bot/error_handlers.php +++ b/core/bot/error_handlers.php @@ -32,7 +32,7 @@ function error_handler($severity, $message, $filename, $lineno) { $logger = 'error_log'; } $logger('Crash incoming, have a detailed backtrace:'); - $logger(debug_backtrace()); + $logger(print_r(debug_backtrace(), true)); throw new ErrorException($message, 0, $severity, $filename, $lineno); } diff --git a/core/bot/importal.php b/core/bot/importal.php index 996d316c..560e7ec6 100644 --- a/core/bot/importal.php +++ b/core/bot/importal.php @@ -1,97 +1,101 @@ £ - // â = 0x00E2 - // <81> = 0x81 - // £ = 0x00A3 - if(strpos($portal, chr(0x00E2) . chr(0x81) . chr(0x00A3)) === 0) { - // Remove strange characters from portal name. - $portal = substr($portal, 3); - debug_log('Strange characters â<81>£ detected and removed from portal name!'); - } - - // Get portal address. - $address = trim($msg_to_rows[4]); - - // Remove country from address, e.g. ", Netherlands" - $address = explode(',',$address,-1); - $address = trim(implode(',',$address)); - - // Portal id - $portal_id = trim($msg_to_rows[(count($msg_to_rows)-1)]); - - // Portal image - $portal_image = $update['message']['entities']['0']['url']; - - } else { - // Invalid input or unknown bot - send message and end. - $msg = '' . getTranslation('invalid_input') . ''; - $msg .= CR . CR . getTranslation('not_supported') . SP . getTranslation('or') . SP . getTranslation('internal_error'); - send_message($update['message']['from']['id'], $msg); - exit(); - } - - // Empty address? Try lookup. - if(empty($address)) { - // Get address. - $addr = get_address($lat, $lon); - $address = format_address($addr); - } - - // Write to log. - debug_log('Detected message from ' . $botname); - debug_log($portal, 'Portal:'); - debug_log($coords, 'Coordinates:'); - debug_log($lat, 'Latitude:'); - debug_log($lon, 'Longitude:'); - debug_log($address, 'Address:'); - debug_log($portal_id, 'Portal id:'); - -?> + +$supported_bots = ['Ingressportalbot', 'PortalMapBot']; +if(!isset($update['message']['via_bot']) || !in_array($update['message']['via_bot']['username'], $supported_bots)) { + // Invalid input or unknown bot - send message and end. + $msg = '' . getTranslation('invalid_input') . ''; + $msg .= CR . CR . getTranslation('not_supported') . SP . getTranslation('or') . SP . getTranslation('internal_error'); + send_message(create_chat_object([$update['message']['chat']['id']]), $msg); + exit(); +} + +// Ingressportalbot icon +$icon = iconv('UCS-4LE', 'UTF-8', pack('V', 0x1F4DC)); +$coords = explode('&pll=',$update['message']['entities']['1']['url'])[1]; +$latlon = explode(',', $coords); +$lat = $latlon[0]; +$lon = $latlon[1]; + +$msg_to_rows = explode(PHP_EOL, $update['message']['text']); + +$parse_bot = $update['message']['via_bot']['username']; + +// Ingressportalbot +if($parse_bot == 'Ingressportalbot') { + // Set portal bot name. + $botname = '@Ingressportalbot'; + + // Get portal name. + $portal = trim(str_replace($icon . 'Portal:', '', $msg_to_rows[0])); + + // Get portal address. + $address = trim(explode(':', $msg_to_rows[1], 2)[1]); + + // Split address? + debug_log($address, 'Address:'); + if(substr_count($address, ',') == 7) { + // Split address into 6 pieces which are separated by comma: + // Street Number, Street, Locality, Sublocality, City, State, ZIP Code, Country + $pieces = explode(',', $address); + $address = trim($pieces[1]) . SP . trim($pieces[0]) . ', ' . trim($pieces[6]) . SP . trim($pieces[4]); + } else if(substr_count($address, ',') == 8) { + // Split address into 7 pieces which are separated by comma: + // Place, Street Number, Street, Locality, Sublocality, City, State, ZIP Code, Country + $pieces = explode(',', $address); + $address = trim($pieces[2]) . SP . trim($pieces[1]) . ', ' . trim($pieces[7]) . SP . trim($pieces[5]); + } + + // Portal id + $portal_id = trim(substr($msg_to_rows[(count($msg_to_rows)-1)], 6)); + + // Portal image + $portal_image = $update['message']['entities']['0']['url']; + +// PortalMapBot +} else if($parse_bot == 'PortalMapBot') { + // Set portal bot name. + $botname = '@PortalMapBot'; + + // Get portal name. + $portal = trim(str_replace('(Intel)','', str_replace('(Scanner)','',$msg_to_rows[0]))); + + // Check for strange characters at the beginn of the portal name: â<81>£ + // â = 0x00E2 + // <81> = 0x81 + // £ = 0x00A3 + if(strpos($portal, chr(0x00E2) . chr(0x81) . chr(0x00A3)) === 0) { + // Remove strange characters from portal name. + $portal = substr($portal, 3); + debug_log('Strange characters â<81>£ detected and removed from portal name!'); + } + + // Get portal address. + $address = trim($msg_to_rows[4]); + + // Remove country from address, e.g. ", Netherlands" + $address = explode(',',$address,-1); + $address = trim(implode(',',$address)); + + // Portal id + $portal_id = trim($msg_to_rows[(count($msg_to_rows)-1)]); + + // Portal image + $portal_image = $update['message']['entities']['0']['url']; + +} + +// Empty address? Try lookup. +if(empty($address)) { + // Get address. + $addr = get_address($lat, $lon); + $address = format_address($addr); +} + +// Write to log. +debug_log('Detected message from ' . $botname); +debug_log($portal, 'Portal:'); +debug_log($coords, 'Coordinates:'); +debug_log($lat, 'Latitude:'); +debug_log($lon, 'Longitude:'); +debug_log($address, 'Address:'); +debug_log($portal_id, 'Portal id:'); diff --git a/core/bot/is_init.php b/core/bot/is_init.php index baa4ce5b..4f6308b5 100644 --- a/core/bot/is_init.php +++ b/core/bot/is_init.php @@ -3,17 +3,22 @@ /* If we're able to, detect the first time this PHP context is initialized. * - **Optional** QoL things can be gated to be run only on this init round. * - Use IS_INIT to gate those things. + * - **Mandatory** code that should get run, but ideally only once should be gated by IS_INIT_OR_WHATEVER instead. * */ if(extension_loaded('apcu') && apcu_enabled()){ - if (!apcu_exists($namespace)){ + if (isset($namespace) && !apcu_exists($namespace)){ apcu_store($namespace, time()); define('IS_INIT', true); + define('IS_INIT_OR_WHATEVER', true); } else { define('IS_INIT', false); + define('IS_INIT_OR_WHATEVER', false); } } else { // If APCu becomes more widely used in the codebase we could flip the default to be the other way around eventually. define('IS_INIT', false); + // In the meanwhile code that should be run anyway, but can be optimized away with APCu should be gated by this. + define('IS_INIT_OR_WHATEVER', true); } diff --git a/core/bot/logic.php b/core/bot/logic.php deleted file mode 100644 index e5bed5df..00000000 --- a/core/bot/logic.php +++ /dev/null @@ -1,16 +0,0 @@ - diff --git a/core/bot/logic/bearer_token.php b/core/bot/logic/bearer_token.php deleted file mode 100644 index e68ae330..00000000 --- a/core/bot/logic/bearer_token.php +++ /dev/null @@ -1,33 +0,0 @@ -BOT_ADMINS)) { - debug_log('Bot access is not restricted! Allowing access for user: ' . CR . $user_id); - $allow_access = true; - $access_granted_by = 'NOT_RESTRICTED'; - }else { - // Admin? - $admins = explode(',', $config->BOT_ADMINS); - if(in_array($user_id,$admins)) { - debug_log('Positive result on access check for Bot Admins'); - debug_log('Bot Admins: ' . $config->BOT_ADMINS); - debug_log('user_id: ' . $user_id); - $access_granted_by = 'BOT_ADMINS'; - debug_log('Allowing access to the bot for user:' . CR . $user_id); - $allow_access = true; - } - } - if($allow_access) { - if($return_access) { - return $access_granted_by; - } else { - return true; - } - } - - // Get all chat files for groups/channels like -100111222333 - // Creators - $creator_chats = array(); - $creator_chats = str_replace(ACCESS_PATH . '/creator','',glob(ACCESS_PATH . '/creator-*')); - - // Admins - $admin_chats = array(); - $admin_chats = str_replace(ACCESS_PATH . '/admins','',glob(ACCESS_PATH . '/admins-*')); - - // Members - $member_chats = array(); - $member_chats = str_replace(ACCESS_PATH . '/members','',glob(ACCESS_PATH . '/members-*')); - - // Restricted - $restricted_chats = array(); - $restricted_chats = str_replace(ACCESS_PATH . '/restricted','',glob(ACCESS_PATH . '/restricted-*')); - - // Kicked - $kicked_chats = array(); - $kicked_chats = str_replace(ACCESS_PATH . '/kicked','',glob(ACCESS_PATH . '/kicked-*')); - - // Access chats - $access_chats = array(); - $access_chats = str_replace(ACCESS_PATH . '/access','',glob(ACCESS_PATH . '/access-*')); - $access_chats = array_merge($access_chats, $creator_chats, $admin_chats, $member_chats, $restricted_chats, $kicked_chats); - - // Add user_id - if (is_file(ACCESS_PATH . '/access' . $user_id)) { - $access_chats[] = $user_id; - } - // Delete duplicates - $access_chats = array_unique($access_chats); - - // Get count of access files. - $access_count = count($access_chats); - - // Check each chat - debug_log('Checking these chats:'); - debug_log($access_chats); - debug_log($access_count, 'Chat count:'); - - // Record why access was granted - $access_granted_by = false; - - $access_file = $afile = "UNDEFINED"; - - // Check access files, otherwise check only BOT_ADMINS as no access files are existing yet. - if($access_count > 0) { - // Check access and permission - foreach($access_chats as $chat) { - // Get chat object - remove comments from filename - // This way some kind of comment like the channel name can be added to the end of the filename, e.g. creator-100123456789-MyPokemonChannel to easily differ between access files :) - // Source: php.net/manual/en/function.intval.php#7707 - preg_match_all('/-?\d+/', $chat, $tg_chat); - $tg_chat=$tg_chat[0][0]; - debug_log("Getting chat object for '$tg_chat'"); - - // Group/channel? - if($chat[0] == '-') { - // Get chat member object and check status - debug_log("Getting user from chat '$tg_chat'"); - $chat_obj = get_chatmember($tg_chat, $user_id); - - // Make sure we get a proper response - if($chat_obj['ok'] == true) { - debug_log('Proper chat object received, continuing with access check.'); - - // Get access file based on user status/role. - debug_log('Role of user ' . $chat_obj['result']['user']['id'] . ' : ' . $chat_obj['result']['status']); - - // Creator - if($chat_obj['result']['status'] == 'creator' && is_file(ROOT_PATH . '/access/creator' . $chat)) { - $access_file = file(ROOT_PATH . '/access/creator' . $chat, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); - $afile = 'creator' . $chat; - - // Admin - } else if($chat_obj['result']['status'] == 'administrator' && is_file(ROOT_PATH . '/access/admins' . $chat)) { - $access_file = file(ROOT_PATH . '/access/admins' . $chat, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); - $afile = 'admins' . $chat; - - // Member - } else if($chat_obj['result']['status'] == 'member' && is_file(ROOT_PATH . '/access/members' . $chat)) { - $access_file = file(ROOT_PATH . '/access/members' . $chat, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); - $afile = 'members' . $chat; - - // Restricted - } else if($chat_obj['result']['status'] == 'restricted' && is_file(ROOT_PATH . '/access/restricted' . $chat)) { - $access_file = file(ROOT_PATH . '/access/restricted' . $chat, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); - $afile = 'restricted' . $chat; - - // Kicked - } else if($chat_obj['result']['status'] == 'kicked' && is_file(ROOT_PATH . '/access/kicked' . $chat)) { - $access_file = file(ROOT_PATH . '/access/kicked' . $chat, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); - $afile = 'kicked' . $chat; - - // Any other user status/role except "left" - } else if($chat_obj['result']['status'] != 'left' && is_file(ROOT_PATH . '/access/access' . $chat)) { - $access_file = file(ROOT_PATH . '/access/access' . $chat, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); - $afile = 'access' . $chat; - - // Ignore "Restricted"? - if($chat_obj['result']['status'] == 'restricted' && in_array('ignore-restricted', $access_file)) { - // Reset access file. - $access_file = NULL; - } - - // Ignore "kicked"? - if($chat_obj['result']['status'] == 'kicked' && in_array('ignore-kicked', $access_file)) { - // Reset access file. - $access_file = NULL; - } - } - - // Debug. - debug_log('Access file:'); - debug_log($access_file); - - // If a config file matching users status was found, check if tutorial is forced - if(is_array($access_file) && $config->TUTORIAL_MODE && $access_granted_by != 'BOT_ADMINS' && $new_user && in_array("force-tutorial",$access_file)) { - $access_file = NULL; - } - - // Check user status/role and permission to access the function - if($chat_obj['result']['user']['id'] == $user_id && is_array($access_file) && in_array($permission,$access_file)) { - debug_log($afile, 'Positive result on access check in file:'); - debug_log($tg_chat, 'Positive result on access check from chat:'); - $allow_access = true; - $access_granted_by = $afile; - break; - } else { - // Deny access - debug_log($afile, 'Negative result on access check in file:'); - debug_log('Continuing with next chat...'); - continue; - } - } else { - // Invalid chat - debug_log('Chat ' . $tg_chat . ' does not exist! Continuing with next chat...'); - continue; - } - // Private chat - } else { - // Get chat object - debug_log("Getting chat object for '$tg_chat'"); - $chat_obj = get_chat($tg_chat); - - // Check chat object for proper response. - if($chat_obj['ok'] == true) { - debug_log('Proper chat object received, continuing with access check.'); - - // Get access file - if(is_file(ROOT_PATH . '/access/access' . $chat)) { - $access_file = file(ROOT_PATH . '/access/access' . $chat, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); - } - $afile = 'access' . $chat; - - // ID matching $chat, private chat type and permission to access the function - if($chat_obj['result']['id'] == $user_id && $chat_obj['result']['type'] == 'private' && is_array($access_file) && in_array($permission,$access_file)) { - debug_log($afile, 'Positive result on access check in file:'); - debug_log($user_id, 'Positive result on access check for user:'); - $allow_access = true; - $access_granted_by = $afile; - break; - } else if($chat_obj['result']['type'] == 'private') { - // Result was ok, but access not granted. Continue with next chat if type is private. - debug_log($afile, 'Negative result on access check in file:'); - debug_log('Continuing with next chat...'); - continue; - } - } else { - // Invalid chat - debug_log('Chat ' . $tg_chat . ' does not exist! Continuing with next chat...'); - continue; - } - } - } - } - - // Result of access check? - // Prepare logging of id, username and/or first_name - $msg = ''; - $msg .= !empty($update[$update_type]['from']['id']) ? 'Id: ' . $update[$update_type]['from']['id'] . CR : ''; - $msg .= !empty($update[$update_type]['from']['username']) ? 'Username: ' . $update[$update_type]['from']['username'] . CR : ''; - $msg .= !empty($update[$update_type]['from']['first_name']) ? 'First Name: ' . $update[$update_type]['from']['first_name'] . CR : ''; - - // Allow or deny access to the bot and log result - if ($allow_access && !$return_result) { - debug_log('Allowing access to the bot for user:' . CR . $msg); - // Return access (BOT_ADMINS or access_file) - if($return_access) { - return $access_granted_by; - } - } else if ($allow_access && $return_result) { - debug_log('Allowing access to the bot for user:' . CR . $msg); - return $allow_access; - } else if (!$allow_access && $return_result) { - debug_log('Denying access to the bot for user:' . CR . $msg); - return $allow_access; - } else { - debug_log('Denying access to the bot for user:' . CR . $msg); - $response_msg = '' . getTranslation('bot_access_denied') . ''; - // Edit message or send new message based on value of $update_type - if ($update_type == 'callback_query') { - // Empty keys. - $keys = []; - - // Telegram JSON array. - $tg_json = array(); - - // Edit message. - $tg_json[] = edit_message($update, $response_msg, $keys, false, true); - - // Answer the callback. - $tg_json[] = answerCallbackQuery($update[$update_type]['id'], getTranslation('bot_access_denied'), true); - - // Telegram multicurl request. - curl_json_multi_request($tg_json); - } else { - send_message($update[$update_type]['from']['id'], $response_msg); - } - exit; - } -} - -?> diff --git a/core/bot/logic/bot_upgrade_check.php b/core/bot/logic/bot_upgrade_check.php deleted file mode 100644 index 26081e98..00000000 --- a/core/bot/logic/bot_upgrade_check.php +++ /dev/null @@ -1,120 +0,0 @@ - $version), CONFIG_PATH . '/config.json'); -} - -/** - * Get current code revision from git state, or 'manual' if no git state exists. - * @return string - */ -function get_rev() -{ - if (file_exists(ROOT_PATH . '/.git/HEAD')){ - $ref = trim(file_get_contents(ROOT_PATH . '/.git/HEAD')); - if (ctype_xdigit($ref)){ - // Is already a hex string and thus valid rev - // Return first 6 digits of the hash - return substr($ref, 0, 6); - } - // strip 'ref: ' to get file path - $ref_file = ROOT_PATH . '/.git/' . substr($ref, 5); - if (file_exists($ref_file)){ - // Return first 6 digits of the hash, this matches our naming of docker image tags - return substr(file_get_contents($ref_file), 0, 6); - } else { - error_log("Git ref found but we cannot resolve it to a revision ({$ref_file}). Was the .git folder mangled?"); - return 'manual'; - } - } else { - debug_log('No .git/HEAD present, marking revision as manual'); - return 'manual'; - } -} - -/** - * Bot upgrade check - * @param $current - * @param $latest - * @return bool: if a manual upgrade is needed - */ -function bot_upgrade_check($current, $latest) -{ - global $config, $metrics, $namespace; - $orig = $current; // we may have to do multiple upgrades - if ($metrics){ - // This is the one place where we have full knowledge of version information & upgrades - debug_log('init upgrade metrics'); - $version_info = $metrics->registerGauge($namespace, 'version_info', 'Schema and revision information', ['current_schema', 'required_schema', 'rev', 'upgraded_timestamp', 'upgraded_from']); - } - - - $upgrade_verdict = null; - $manual_upgrade_verdict = null; - // Same version? - if($current == $latest) { - $upgrade_verdict = false; - $manual_upgrade_verdict = false; - } else { - $upgrade_verdict = true; - if ($metrics && IS_INIT){ - // record initial version even if we don't do upgrades. - $version_info->set(1, [$current, $latest, get_rev(), null, null]); - } - // Check if upgrade files exist. - $upgrade_files = array(); - $upgrade_files = str_replace(UPGRADE_PATH . '/','', glob(UPGRADE_PATH . '/*.sql')); - if(is_array($upgrade_files) && count($upgrade_files) > 0) { - // Check each sql filename. - foreach ($upgrade_files as $ufile) - { - $target = str_replace('.sql', '', $ufile); - // Skip every older sql file from array. - if($target <= $current) { - continue; - } else { - if ($config->UPGRADE_SQL_AUTO){ - info_log('PERFORMING AUTO SQL UPGRADE: ' . UPGRADE_PATH . '/' . $ufile, '!'); - require_once('sql_utils.php'); - if (run_sql_file(UPGRADE_PATH . '/' . $ufile)) { - $manual_upgrade_verdict = false; - upgrade_config_version($target); - if ($metrics){ - $version_info->set(1, [$target, $latest, get_rev(), time(), $current]); - } - $current = $target; - } else { - $manual_upgrade_verdict = true; - $error = 'AUTO UPGRADE FAILED: ' . UPGRADE_PATH . '/' . $ufile; - throw new Exception($error); - } - } else { - $manual_upgrade_verdict = true; - debug_log("There's a schema upgrade to {$target} we could have run, but auto-upgrades have been disabled!"); - } - } - } - } else { - // No upgrade files found! Since the version now would only go up with upgrade files, something is off. - // It could be the user has bumped the VERSION file manually, or omitted upgrade files. - $error = 'NO SQL UPGRADE FILES FOUND FOR LATEST SCHEMA, THIS SHOULD NOT HAPPEN'; - throw new Exception($error); - } - } - // If previous sql upgrades had to be done and were successful, update also pokemon table - if($upgrade_verdict === true && $manual_upgrade_verdict === false) { - require_once(ROOT_PATH . '/mods/getdb.php'); - } - - // Signal whether manual action is required or not. - if ($manual_upgrade_verdict === true){ - $error = "The bot has pending schema upgrades ({$current} -> {$latest}) but you've disabled automatic upgrades. Nothing will work until you go do the upgrade(s) manually. You'll find them in the dir sql/upgrade/"; - throw new Exception($error); - } -} diff --git a/core/bot/logic/date_util.php b/core/bot/logic/date_util.php deleted file mode 100644 index 137bbacc..00000000 --- a/core/bot/logic/date_util.php +++ /dev/null @@ -1,105 +0,0 @@ -format('Y-m-d'); -} - -/** - * Get current utc datetime. - * @param $format - * @return string - */ -function utcnow($format = 'Y-m-d H:i:s') -{ - // Create a object with UTC timezone - $datetime = new DateTime('now', new DateTimeZone('UTC')); - - return $datetime->format($format); -} - - -/** - * Format utc time from datetime value. - * @param $datetime_value - * @param $format - * @return string - */ -function utctime($datetime_value, $format = 'H:i') -{ - // Create a object with UTC timezone - $datetime = new DateTime($datetime_value, new DateTimeZone('UTC')); - - return $datetime->format($format); -} - -/** - * Get date from datetime value. - * @param $datetime_value - * @param $tz - * @return string - */ -function dt2date($datetime_value, $tz = NULL) -{ - global $config; - if($tz == NULL){ - $tz = $config->TIMEZONE; - } - // Create a object with UTC timezone - $datetime = new DateTime($datetime_value, new DateTimeZone('UTC')); - - // Change the timezone of the object without changing it's time - $datetime->setTimezone(new DateTimeZone($tz)); - - return $datetime->format('Y-m-d'); -} - -/** - * Get time from datetime value. - * @param $datetime_value - * @param $format - * @param $tz - * @return string - */ -function dt2time($datetime_value, $format = 'H:i', $tz = NULL) -{ - global $config; - if($tz == NULL){ - $tz = $config->TIMEZONE; - } - // Create a object with UTC timezone - $datetime = new DateTime($datetime_value, new DateTimeZone('UTC')); - - // Change the timezone of the object without changing it's time - $datetime->setTimezone(new DateTimeZone($tz)); - - return $datetime->format($format); -} - -function tz_diff($src = 'UTC', $dst = NULL) { - global $config; - if($dst == NULL){ - $dst = $config->TIMEZONE; - } - $dateTimeZoneSrc = new DateTimeZone($src); - $dateTimeZoneDst = new DateTimeZone($dst); - $dateTimeSrc = new datetime('now',$dateTimeZoneSrc); - $diff = $dateTimeZoneDst->getOffset($dateTimeSrc); - - $hours = str_pad(floor($diff/60/60),2,'0',STR_PAD_LEFT); - $minutes = str_pad(floor(($diff-$hours*60*60)/60),2,'0',STR_PAD_LEFT); - if($diff < 0) { - $res = '-'.$hours.':'.$minutes; - }else { - $res = '+'.$hours.':'.$minutes; - } - return $res; -} -?> diff --git a/core/bot/logic/debug.php b/core/bot/logic/debug.php deleted file mode 100644 index af5e4b1f..00000000 --- a/core/bot/logic/debug.php +++ /dev/null @@ -1,110 +0,0 @@ -DEBUG === false){ - return; - } - generic_log($message, $type, $logfile = $config->DEBUG_LOGFILE); -} - -/** - * Write cleanup log. - * @param $message - * @param string $type - */ -function cleanup_log($message, $type = '*'){ - global $config; - // Write to log only if cleanup logging is enabled. - if ($config->CLEANUP_LOG === false){ - return; - } - generic_log($message, $type, $logfile = $config->CLEANUP_LOGFILE); -} - -/** - * Write sql debug log. - * @param $message - * @param string $type - */ -function debug_log_sql($message, $type = '%'){ - global $config; - // Write to log only if debug is enabled. - if ($config->DEBUG_SQL === false){ - return; - } - generic_log($message, $type, $config->DEBUG_SQL_LOGFILE); -} - -/** - * Write incoming stream debug log. - * @param $message - * @param string $type - */ -function debug_log_incoming($message, $type = '<'){ - global $config; - // Write to log only if debug is enabled. - if ($config->DEBUG_INCOMING === false){ - return; - } - generic_log($message, $type, $config->DEBUG_INCOMING_LOGFILE); -} - -/** - * Write INFO level log. - * @param $message - * @param string $type - */ -function info_log($message, $type = '[I]'){ - global $config; - // Write to log only if info logging is enabled. - if ($config->LOGGING_INFO === false){ - return; - } - generic_log($message, $type, $config->LOGGING_INFO_LOGFILE); -} diff --git a/core/bot/logic/download_Portal_Image.php b/core/bot/logic/download_Portal_Image.php deleted file mode 100644 index 61aef5cd..00000000 --- a/core/bot/logic/download_Portal_Image.php +++ /dev/null @@ -1,42 +0,0 @@ - diff --git a/core/bot/logic/geo_api.php b/core/bot/logic/geo_api.php deleted file mode 100644 index 94e0383c..00000000 --- a/core/bot/logic/geo_api.php +++ /dev/null @@ -1,244 +0,0 @@ -MAPS_LOOKUP && !empty($config->MAPS_API_KEY)) { - // Init defaults. - $location = []; - $location['street'] = ''; - $location['street_number'] = ''; - $location['postal_code'] = ''; - $location['district'] = ''; - - // Set maps geocode url. - $language = strtolower($config->LANGUAGE_PUBLIC); - $MapsApiKey = $config->MAPS_API_KEY; - $url = 'https://maps.googleapis.com/maps/api/geocode/json?latlng=' . $lat . ',' . $lon . '&language=' . $language; - $url .= '&key=' . $MapsApiKey; - - // Curl request. - $curl = curl_init($url); - curl_setopt($curl, CURLOPT_HEADER, false); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - - // Proxy server? - // Use Proxyserver for curl if configured - if ($config->CURL_USEPROXY) { - curl_setopt($curl, CURLOPT_PROXY, $config->CURL_PROXYSERVER); - } - - // Curl response. - $json_response = curl_exec($curl); - - // Write request and response to log. - debug_log($url, 'G>'); - debug_log($json_response, 'status) && $data->status == 'OK' && !empty($data->results)) { - - // Init vars. - $locality = ''; - $sublocalityLv2 = ''; - $sublocality = ''; - - // Iterate each result. - foreach ($data->results as $result) { - - // Check for address components. - if (!empty($result->address_components)) { - // Iterate each address component. - foreach ($result->address_components as $address_component) { - - // Street found. - if (in_array('route', $address_component->types) && !empty($address_component->long_name)) { - // Set street by first found. - $location['street'] = empty($location['street']) ? $address_component->long_name : $location['street']; - } - - // Street number found. - if (in_array('street_number', $address_component->types) && !empty($address_component->long_name)) { - // Set street by first found. - $location['street_number'] = empty($location['street_number']) ? $address_component->long_name : $location['street_number']; - } - - // Postal code found. - if (in_array('postal_code', $address_component->types) && !empty($address_component->long_name)) { - // Set street by first found. - $location['postal_code'] = empty($location['postal_code']) ? $address_component->long_name : $location['postal_code']; - } - - // Sublocality level2 found. - if (in_array('sublocality_level_2', $address_component->types) && !empty($address_component->long_name)) { - // Set sublocality level 2 by first found. - $sublocalityLv2 = empty($sublocalityLv2) ? $address_component->long_name : $sublocalityLv2; - } - - // Sublocality found. - if (in_array('sublocality', $address_component->types) && !empty($address_component->long_name)) { - // Set sublocality by first found. - $sublocality = empty($sublocality) ? $address_component->long_name : $sublocality; - } - - // Locality found. - if (in_array('locality', $address_component->types) && !empty($address_component->long_name)) { - // Set sublocality by first found. - $locality = empty($sublocality) ? $address_component->long_name : $sublocality; - } - } - } - break; - } - - // Set district by priority. - if (!empty($sublocalityLv2)) { - $location['district'] = $sublocalityLv2; - - } else if ($sublocality) { - $location['district'] = $sublocality; - - } else if ($locality) { - $location['district'] = $locality; - } - - // Rename street responses. - switch ($location['street']) { - case 'Unnamed Road': - $location['street'] = getPublicTranslation('forest'); - break; - } - - // Return the location array. - return $location; - - // No valid data received. - } else { - return false; - } - - // OpenStreetMap lookup? - } elseif($config->OSM_LOOKUP) { - // Init defaults. - $location = []; - $location['street'] = ''; - $location['street_number'] = ''; - $location['postal_code'] = ''; - $location['district'] = ''; - - // Set maps geocode url. - $language = strtolower($config->LANGUAGE_PUBLIC); - $MapsApiKey = $config->MAPS_API_KEY; - $url = 'https://nominatim.openstreetmap.org/reverse?lat=' . $lat . '&lon=' . $lon . '&format=json&accept-language=' . $language; - - // Curl request. - $curl = curl_init($url); - curl_setopt($curl, CURLOPT_HTTPHEADER, array("User-Agent: PokemonRaidBot")); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - - // Proxy server? - // Use Proxyserver for curl if configured - if ($config->CURL_USEPROXY) { - curl_setopt($curl, CURLOPT_PROXY, $config->CURL_PROXYSERVER); - } - - // Curl response. - $json_response = curl_exec($curl); - - // Write request and response to log. - debug_log($url, 'OSM>'); - debug_log($json_response, '= $columns) { - $row++; - $col = 0; - } - } - return $result; -} - - -/** - * Universal key. - * @param $keys - * @param $id - * @param $action - * @param $arg - * @param $text - * @return array - */ -function universal_inner_key($keys, $id, $action, $arg, $text = '0') -{ - $keys = array( - 'text' => $text, - 'callback_data' => $id . ':' . $action . ':' . $arg - ); - - // Write to log. - //debug_log($keys); - - return $keys; -} - -/** - * Universal key. - * @param $keys - * @param $id - * @param $action - * @param $arg - * @param $text - * @return array - */ -function universal_key($keys, $id, $action, $arg, $text = '0') -{ - $keys[] = [ - array( - 'text' => $text, - 'callback_data' => $id . ':' . $action . ':' . $arg - ) - ]; - - // Write to log. - //debug_log($keys); - - return $keys; -} - - -/** - * Share keys. - * @param $id - * @param $action - * @param $update - * @param $chats - * @param $prefix_text - * @param $hide - * @return array - */ -function share_keys($id, $action, $update, $chats = '', $prefix_text = '', $hide = false, $level = '') -{ - global $config; - // Check access. - $share_access = bot_access_check($update, 'share-any-chat', true); - - // Add share button if not restricted to allow sharing to any chat. - if ($share_access == true && $hide == false) { - debug_log('Adding general share key to inline keys'); - // Set the keys. - $keys[] = [ - [ - 'text' => getTranslation('share'), - 'switch_inline_query' => basename(ROOT_PATH) . ':' . strval($id) - ] - ]; - } - - // Add buttons for predefined sharing chats. - // Default SHARE_CHATS or special chat list via $chats? - if(!empty($chats)) { - debug_log($chats, 'Got specific chats to share to:'); - $chats = explode(',', $chats); - } else { - if(!empty($level)) { - // find chats to share ourselves, if we can - debug_log($level, 'Did not get specific chats to share to, checking level specific for: '); - $level_chat = 'SHARE_CHATS_LEVEL_' . $level; - if(!empty($config->{$level_chat})) { - $chats = explode(',', $config->{$level_chat}); - debug_log($chats, 'Found level specific chats to share to: '); - } else { - $chats = explode(',', $config->SHARE_CHATS); - debug_log($chats, 'Chats not specified for level, sharing to globals: '); - } - } else { - $chats = explode(',', $config->SHARE_CHATS); - debug_log($chats, 'Level not given, sharing to globals: '); - } - } - // Add keys for each chat. - if(!empty($chats)){ - foreach($chats as $chat) { - // Get chat object - debug_log("Getting chat object for '" . $chat . "'"); - $chat_obj = get_chat($chat); - - // Check chat object for proper response. - if ($chat_obj['ok'] == true) { - debug_log('Proper chat object received, continuing to add key for this chat: ' . $chat_obj['result']['title']); - $keys[] = [ - [ - 'text' => $prefix_text . getTranslation('share_with') . ' ' . $chat_obj['result']['title'], - 'callback_data' => $id . ':' . $action . ':' . $chat - ] - ]; - } - } - } else { - debug_log('Aint got any chats to share to!'); - } - - return $keys; -} - -?> diff --git a/core/bot/logic/language.php b/core/bot/logic/language.php deleted file mode 100644 index e1c5038d..00000000 --- a/core/bot/logic/language.php +++ /dev/null @@ -1,182 +0,0 @@ -LANGUAGE_PUBLIC); - - return $translation; -} - -/** - * Gets a table translation out of the json file. - * @param string $text - * @param bool $override - * @param string $override_language - * @return string translation - */ -function getTranslation($text, $override = false, $override_language = '') -{ - global $config; - debug_log($text,'T:'); - $translation = ''; - $text = trim($text); - - $language = ''; - // Override language? - if($override == true && $override_language != '') { - $language = $override_language; - } else { - $language = USERLANGUAGE; - } - - // Pokemon name? - if(strpos($text, 'pokemon_id_') === 0) { - // Translation filename - $tfile = CORE_LANG_PATH . '/pokemon.json'; - - // Get ID from string - e.g. 150 from pokemon_id_150 - $pokemon_id = substr($text, strrpos($text, '_') + 1); - - // Make sure we have a valid id. - if(is_numeric($pokemon_id) && $pokemon_id > 0) { - $str = file_get_contents($tfile); - - $json = json_decode($str, true); - if(!isset($json[$text][$language])) { - // If translation wasn't found, try to use english as fallback - $language = DEFAULT_LANGUAGE; - } - if(!isset($json[$text][$language])) { - $translation = false; - }else { - $translation = $json[$text][$language]; - } - - // Return false - } else { - debug_log($pokemon_id,'T: Received invalid pokemon id for translation:'); - $translation = false; - } - - // Pokemon form? - } else if(strpos($text, 'pokemon_form_') === 0) { - // Translation filename - $tfile = CORE_LANG_PATH . '/pokemon_forms.json'; - - $str = file_get_contents($tfile); - $json = json_decode($str, true); - - // Pokemon moves? - } else if(strpos($text, 'pokemon_move_') === 0) { - // Translation filename - $tfile = CORE_LANG_PATH . '/pokemon_moves.json'; - - $str = file_get_contents($tfile); - $json = json_decode($str, true); - - // Custom language file. - } else if(is_file(CUSTOM_PATH . '/language.json')) { - $tfile = CUSTOM_PATH . '/language.json'; - - $str = file_get_contents($tfile); - $json = json_decode($str, true); - } - - // Other translation - if(!(isset($json[$text]))){ - // Specific translation file? - // E.g. Translation = hello_world_123, then check if hello_world.json exists. - if(is_file(BOT_LANG_PATH . '/' . substr($text, 0, strrpos($text, '_')) . '.json')) { - // Translation filename - $tfile = BOT_LANG_PATH . '/' . substr($text, 0, strrpos($text, '_')) . '.json'; - - $str = file_get_contents($tfile); - $json = json_decode($str, true); - - // Core language file. - if(!(isset($json[$text]))){ - // Translation filename - $tfile = CORE_LANG_PATH . '/language.json'; - - // Make sure file exists. - if(is_file($tfile)) { - $str = file_get_contents($tfile); - $json = json_decode($str, true); - } - } - } - - // Bot language file. - if(!(isset($json[$text]))){ - // Translation filename - $tfile = BOT_LANG_PATH . '/language.json'; - - // Make sure file exists. - if(is_file($tfile)) { - $str = file_get_contents($tfile); - $json = json_decode($str, true); - } - } - - // Translation not in core or bot language file? - Try other core files. - if(!(isset($json[$text]))){ - // Get all bot specific language files - $langfiles = glob(CORE_LANG_PATH . '/*.json'); - - // Find translation in the right file - foreach($langfiles as $file) { - $tfile = $file; - $str = file_get_contents($file); - $json = json_decode($str, true); - // Exit foreach once found - if(isset($json[$text])) { - break; - } - } - } - - // Translation not in core or bot language file? - Try other bot files. - if(!(isset($json[$text]))){ - // Get all bot specific language files - $langfiles = glob(BOT_LANG_PATH . '/*.json'); - - // Find translation in the right file - foreach($langfiles as $file) { - $tfile = $file; - $str = file_get_contents($file); - $json = json_decode($str, true); - // Exit foreach once found - if(isset($json[$text])) { - break; - } - } - } - } - - // Debug log translation file - debug_log($tfile,'T:'); - - // Return pokemon name or translation - if(strpos($text, 'pokemon_id_') === 0) { - return $translation; - } else { - // Fallback to English when there is no language key or translation is not yet done. - if(isset($json[$text][$language]) && $json[$text][$language] != 'TRANSLATE'){ - $translation = $json[$text][$language]; - } else { - $language = DEFAULT_LANGUAGE; - if(isset($json[$text][$language])){ - $translation = $json[$text][$language]; - }else { - $translation = ''; - } - } - //debug_log($translation,'T:'); - return $translation; - } -} diff --git a/core/bot/logic/update_user.php b/core/bot/logic/update_user.php deleted file mode 100644 index 8ee02996..00000000 --- a/core/bot/logic/update_user.php +++ /dev/null @@ -1,21 +0,0 @@ - diff --git a/core/bot/logic/update_userdb.php b/core/bot/logic/update_userdb.php deleted file mode 100644 index ce73e2ae..00000000 --- a/core/bot/logic/update_userdb.php +++ /dev/null @@ -1,80 +0,0 @@ -prepare( - " - INSERT INTO users - SET user_id = :id, - nick = :nick, - name = :name, - lang = :lang, - auto_alarm = :auto_alarm - ON DUPLICATE KEY - UPDATE nick = :nick, - name = :name, - lang = IF(lang_manual = 1, lang, :lang), - auto_alarm = IF(:auto_alarm = 1, 1, auto_alarm) - " - ); - $alarm_setting = ($config->RAID_AUTOMATIC_ALARM ? 1 : 0); - $stmt->bindParam(':id', $id); - $stmt->bindParam(':nick', $nick); - $stmt->bindParam(':name', $name); - $stmt->bindParam(':lang', $lang); - $stmt->bindParam(':auto_alarm', $alarm_setting); - $stmt->execute(); - - return 'Updated user ' . $nick; -} -?> diff --git a/core/bot/modules.php b/core/bot/modules.php index 7aac7a9b..68b8d08a 100644 --- a/core/bot/modules.php +++ b/core/bot/modules.php @@ -1,26 +1,23 @@ TUTORIAL_MODE && isset($update['callback_query']['from']['id']) && new_user($update['callback_query']['from']['id']) && $data['action'] != "tutorial") { - answerCallbackQuery($update['callback_query']['id'], getTranslation("tutorial_vote_failed")); - $dbh = null; - exit(); +if(isset($update['callback_query']['from']['id']) && new_user($update['callback_query']['from']['id']) && $data[0] != 'tutorial') { + answerCallbackQuery($update['callback_query']['id'], getTranslation("tutorial_vote_failed")); + exit(); } // Set module path by sent action name. -$module = ROOT_PATH . '/mods/' . basename($data['action']) . '.php'; +$module = ROOT_PATH . '/mods/' . $data[0] . '.php'; // Write module to log. debug_log($module); // Check if the module file exists. if (file_exists($module)) { - // Dynamically include module file and exit. - include_once($module); - exit(); - -// Module file is missing. -} else { - // Write to log. - debug_log('No action'); + // Dynamically include module file and exit. + include_once($module); + exit(); } +// Module file is missing. +// Write to log. +debug_log('No action'); diff --git a/core/bot/paths.php b/core/bot/paths.php index 6fcf4ca9..82dd84fc 100644 --- a/core/bot/paths.php +++ b/core/bot/paths.php @@ -5,9 +5,7 @@ // Core Paths define('CORE_TG_PATH', CORE_PATH . '/telegram'); define('CORE_BOT_PATH', CORE_PATH . '/bot'); -define('CORE_LANG_PATH', CORE_PATH . '/lang'); define('CORE_COMMANDS_PATH', CORE_PATH . '/commands'); -define('CORE_CLASS_PATH', CORE_PATH . '/class'); // Bot Paths define('CONFIG_PATH', ROOT_PATH . '/config'); diff --git a/core/bot/requirements.php b/core/bot/requirements.php index 6c8517ed..9e11cd4e 100644 --- a/core/bot/requirements.php +++ b/core/bot/requirements.php @@ -20,22 +20,17 @@ require_once(CUSTOM_PATH . '/constants.php'); } -// Core Constants -require_once(CORE_BOT_PATH . '/constants.php'); - // Bot Constants -if(is_file(ROOT_PATH . '/constants.php')) { - require_once(ROOT_PATH . '/constants.php'); -} +require_once(ROOT_PATH . '/constants.php'); // Config require_once(CORE_BOT_PATH . '/config.php'); // Logging functions -require_once(CORE_BOT_PATH . '/logic/debug.php'); +require_once(ROOT_PATH . '/logic/debug.php'); // SQL Utils -require_once(CORE_BOT_PATH . '/logic/sql_utils.php'); +require_once(ROOT_PATH . '/logic/sql_utils.php'); // Optionally load Composer autoloads. It's not yet a strict requirement for the majority of the project // We load these as soon as possible so that anything after them can benefit, but we need config, logging and so on to be functional. @@ -52,8 +47,11 @@ // Database connection require_once(CORE_BOT_PATH . '/db.php'); -// Core Logic -require_once(CORE_BOT_PATH . '/logic.php'); +// Bot Logic +require_once(ROOT_PATH . '/logic.php'); + +// User logic +require_once(CORE_BOT_PATH . '/user.php'); // Telegram Core require_once(CORE_TG_PATH . '/functions.php'); @@ -61,11 +59,5 @@ // Timezone require_once(CORE_BOT_PATH . '/timezone.php'); -// Bot Logic -if(is_file(ROOT_PATH . '/logic.php')) { - require_once(ROOT_PATH . '/logic.php'); -} - // Bot version require_once(CORE_BOT_PATH . '/version.php'); - diff --git a/core/bot/user.php b/core/bot/user.php new file mode 100644 index 00000000..95bfc674 --- /dev/null +++ b/core/bot/user.php @@ -0,0 +1,347 @@ + [], + 'grantedBy' => '', + ]; + public $userLanguage = ''; + public $ddosCount = 0; + public $userId = 0; + + /** + * Read user privileges from db + * @param array $update Update array from Telegram + */ + public function initPrivileges() { + $q = my_query('SELECT privileges FROM users WHERE user_id = ? LIMIT 1', [$this->userId]); + $result = $q->fetch(); + if($result['privileges'] === NULL) { + // New users haven't probably used any command that would trigger privilegeCheck so we run it here + $this->privilegeCheck(); + return; + } + $privilegesArray = json_decode($result['privileges'], true); + $this->userPrivileges['privileges'] = $privilegesArray['privileges'] ?? []; + $this->userPrivileges['grantedBy'] = $privilegesArray['grantedBy'] ?? []; + } + + /** + * Run privilege check for Telegram user and save them for later use. + * @param array $update Update array from Telegram + */ + public function privilegeCheck() { + global $config; + // Write to log. + debug_log('Checking access for ID: ' . $this->userId); + + // Public access? + if(empty($config->BOT_ADMINS)) { + debug_log('Bot access is not restricted! Allowing access for user: ' . CR . $this->userId); + $this->userPrivileges['grantedBy'] = 'NOT_RESTRICTED'; + my_query('UPDATE users SET privileges = ? WHERE user_id = ? LIMIT 1', [json_encode(['grantedBy' => 'NOT_RESTRICTED']), $this->userId]); + return; + } + // Admin? + $admins = explode(',', $config->BOT_ADMINS); + if(in_array($this->userId, $admins)) { + debug_log('Positive result on access check for Bot Admins'); + debug_log('Bot Admins: ' . $config->BOT_ADMINS); + debug_log('user_id: ' . $this->userId); + my_query('UPDATE users SET privileges = ? WHERE user_id = ? LIMIT 1', [json_encode(['grantedBy' => 'BOT_ADMINS']), $this->userId]); + $this->userPrivileges['grantedBy'] = 'BOT_ADMINS'; + return; + } + + // Map telegram roles to access file names + $telegramRoles = [ + 'creator' => 'creator', + 'administrator' => 'admins', + 'member' => 'members', + 'restricted' => 'restricted', + 'kicked' => 'kicked', + ]; + + $privilegeArray = [ + 'privileges' => [], + 'grantedBy' => '', + ]; + $accessFilesList = $tg_json = []; + foreach(glob(ACCESS_PATH . '/*') as $filePath) { + $filename = str_replace(ACCESS_PATH . '/', '', $filePath); + // If bot name is set read only bot specific files + if (isset($_GET['bot_name']) && !empty($_GET['bot_name']) && !preg_match('/^' . $_GET['bot_name'] . '/', $filename)) + continue; + // Get chat object - remove comments from filename + // This way some kind of comment like the channel name can be added to the end of the filename, e.g. creator-100123456789-MyPokemonChannel to easily differ between access files :) + preg_match('/(access)('.$this->userId.')|(access|creator|admins|members|restricted|kicked)(-[0-9]+)/', '-' . $filename, $result); + if(empty($result[0])) continue; + // User specific access file found? + if(!empty($result[1])) { + $privilegeList = file($filePath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + debug_log($filename, 'Positive result on access check in file:'); + $this->userPrivileges = [ + 'privileges' => $privilegeList, + 'grantedBy' => $filename, + ]; + return; + } + // Group/channel? + $role = $result[3]; + $tg_chat = $result[4]; + // Save the full filename (with possible comments) to an array for later use + $accessFilesList[$role.$tg_chat] = $filename; + debug_log('Asking Telegram if user is a member of chat \'' . $tg_chat . '\''); + if(!isset($tg_json[$tg_chat])) $tg_json[$tg_chat] = get_chatmember($tg_chat, $this->userId, true); // Get chat member object and check status + } + $accessChats = curl_json_multi_request($tg_json); + + // Loop through different chats + foreach($accessChats as $chatId => $chatObj) { + $userStatus = $chatObj['result']['status']; + if(!isset($chatObj['ok']) or $chatObj['ok'] != true or $userStatus == 'left' or !array_key_exists($userStatus, $telegramRoles)){ + // Deny access + debug_log($chatId, 'Negative result on access check for chat:'); + debug_log('Continuing with next chat...'); + continue; + } + // Object contains response from Telegram + debug_log('Proper chat object received, continuing with access check.'); + debug_log('Role of user ' . $chatObj['result']['user']['id'] . ' : ' . $chatObj['result']['status']); + + // Get access file based on user status/role. + $roleAndChat = $telegramRoles[$userStatus] . $chatId; + if(array_key_exists($roleAndChat, $accessFilesList)) { + $privilegeList = file(ACCESS_PATH . '/' . $accessFilesList[$roleAndChat], FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + $accessFile = $accessFilesList[$roleAndChat]; + + // Any other user status/role except "left" + } else if(array_key_exists('access' . $chatId, $accessFilesList)) { + $privilegeList = file(ACCESS_PATH . '/' . $accessFilesList['access' . $chatId], FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + $accessFile = $accessFilesList['access' . $chatId]; + + // Ignore "Restricted" or "kicked"? + if( ($userStatus == 'restricted' && in_array('ignore-restricted', $privilegeList)) + or ($userStatus == 'kicked' && in_array('ignore-kicked', $privilegeList))) { + $privilegeList = NULL; + } + // Debug. + debug_log('Access file:'); + debug_log($privilegeList); + } + + // Save privileges if found + if(isset($privilegeList) && is_array($privilegeList)) { + debug_log($accessFile, 'Positive result on access check in file:'); + $privilegeArray = [ + 'privileges' => $privilegeList, + 'grantedBy' => $accessFile, + ]; + break; + } + // Deny access + debug_log($chatId, 'Negative result on access check for chat:'); + debug_log('Continuing with next chat...'); + } + my_query('UPDATE users SET privileges = ? WHERE user_id = ? LIMIT 1', [json_encode($privilegeArray), $this->userId]); + $this->userPrivileges = $privilegeArray; + } + + /** + * Check users privileges for a specific action. Exits by default if access is denied. + * @param string $permission Permission to check + * @param bool $return_result Return the result of privilege check + * @param bool $new_user Has user completed tutorial or not + * @return bool|void + */ + public function accessCheck($permission = 'access-bot', $return_result = false, $new_user = false) { + if(!$new_user && in_array($permission, $this->userPrivileges['privileges']) or $this->userPrivileges['grantedBy'] === 'BOT_ADMINS' or $this->userPrivileges['grantedBy'] === 'NOT_RESTRICTED') { + return true; + } + debug_log('Denying access to the bot for user'); + + if($return_result) + return false; + + $this->denyAccess(); + } + + /** + * Send Access denied -message to user and exit + * @return void + */ + public function denyAccess() { + global $update; + $response_msg = '' . getTranslation('bot_access_denied') . ''; + // Edit message or send new message based on type of received call + if ($update['type'] != 'callback_query') { + send_message(create_chat_object([$update['message']['chat']['id']]), $response_msg); + exit; + } + $keys = []; + + // Telegram JSON array. + $tg_json = array(); + $tg_json[] = edit_message($update, $response_msg, $keys, false, true); + $tg_json[] = answerCallbackQuery($update['callback_query']['id'], getTranslation('bot_access_denied'), true); + + curl_json_multi_request($tg_json); + exit; + } + + /** + * Raid access check. + * @param int $raidId + * @param string $permission + * @param bool $return_result + * @return bool + */ + public function raidaccessCheck($raidId, $permission, $return_result = false) + { + global $update; + // Default: Deny access to raids + $raid_access = false; + + // Build query. + $rs = my_query(' + SELECT user_id + FROM raids + WHERE id = ? + ', [$raidId] + ); + + $raid = $rs->fetch(); + + // Check permissions + if ($rs->rowCount() == 0 or $this->userId != $raid['user_id']) { + // Check "-all" permission + debug_log('Checking permission:' . $permission . '-all'); + $permission = $permission . '-all'; + return $this->accessCheck($permission, $return_result); + } + // Check "-own" permission + debug_log('Checking permission:' . $permission . '-own'); + $permission_own = $permission . '-own'; + $permission_all = $permission . '-all'; + $raid_access = $this->accessCheck($permission_own, true); + + if($raid_access) { + return $this->accessCheck($permission_own, $return_result); + } + // Check "-all" permission if we get "access denied" + // Maybe necessary if user has only "-all" configured, but not "-own" + debug_log('Permission check for ' . $permission_own . ' failed! Maybe the access is just granted via ' . $permission . '-all ?'); + debug_log('Checking permission:' . $permission_all); + return $this->accessCheck($permission_all, $return_result); + } + + /** + * Update users info if allowed + * @param $update + */ + public function updateUser($update) + { + $this->userId = $update[$update['type']]['from']['id']; + // Check DDOS count + if ($this->ddosCount >= 2) return; + // Update the user. + $userUpdate = $this->updateUserdb($update); + + // Write to log. + debug_log('Update user: ' . $userUpdate); + } + + /** + * Define userlanguage + * @param $update + */ + public function defineUserLanguage($update) { + global $config; + // Write to log. + debug_log('Language Check'); + + // Get language from user - otherwise use language from config. + if ($config->LANGUAGE_PRIVATE != '') { + // Set user language to language from config. + $this->userLanguage = $config->LANGUAGE_PRIVATE; + return; + } + // Message or callback? + + $language_code = ''; + $q = my_query('SELECT lang FROM users WHERE user_id = ? LIMIT 1', [$this->userId]); + $res = $q->fetch(); + $language_code = $res['lang']; + + // Get and define userlanguage. + $languages = $GLOBALS['languages']; + + // Get languages from normal translation. + $userlanguage = (array_key_exists($language_code, $languages)) ? $languages[$language_code] : DEFAULT_LANGUAGE; + + debug_log('User language: ' . $userlanguage); + $this->userLanguage = $userlanguage; + } + + /** + * Update users info into database. + * @param $update + */ + private function updateUserdb($update) + { + global $config; + + $msg = $update[$update['type']]['from']; + + if (empty($msg['id'])) { + debug_log('No id', '!'); + debug_log($update, '!'); + return false; + } + $id = $this->userId; + + $name = ''; + $sep = ''; + + if (isset($msg['first_name'])) { + $name = $msg['first_name']; + $sep = ' '; + } + + if (isset($msg['last_name'])) { + $name .= $sep . $msg['last_name']; + } + + $nick = (isset($msg['username'])) ? $msg['username'] : ''; + + $lang = (isset($msg['language_code']) && array_key_exists($msg['language_code'], $GLOBALS['languages'])) ? $msg['language_code'] : 'en'; + + $alarm_setting = ($config->RAID_AUTOMATIC_ALARM ? 1 : 0); + + // Create or update the user. + my_query(' + INSERT INTO users + SET user_id = :id, + nick = :nick, + name = :name, + lang = :lang, + auto_alarm = :auto_alarm + ON DUPLICATE KEY + UPDATE nick = :nick, + name = :name, + lang = IF(lang_manual = 1, lang, :lang), + auto_alarm = IF(:auto_alarm = 1, 1, auto_alarm) + ', + [ + ':id' => $id, + ':nick' => $nick, + ':name' => $name, + ':lang' => $lang, + ':auto_alarm' => $alarm_setting, + ] + ); + + return 'Updated user ' . $nick; + } +} diff --git a/core/bot/userlanguage.php b/core/bot/userlanguage.php deleted file mode 100644 index 48f21ee5..00000000 --- a/core/bot/userlanguage.php +++ /dev/null @@ -1,38 +0,0 @@ -LANGUAGE_PRIVATE == '') { - // Message or callback? - if(isset($update['message']['from'])) { - $from = $update['message']['from']; - } else if(isset($update['callback_query']['from'])) { - $from = $update['callback_query']['from']; - } else if(isset($update['inline_query']['from'])) { - $from = $update['inline_query']['from']; - } - if(isset($from)) { - $q = my_query("SELECT lang FROM users WHERE user_id='".$from['id']."' LIMIT 1"); - $res = $q->fetch(); - $language_code = $res['lang']; - }else { - $language_code = ''; - } - - // Get and define userlanguage. - $languages = $GLOBALS['languages']; - - // Get languages from normal translation. - if(array_key_exists($language_code, $languages)) { - $userlanguage = $languages[$language_code]; - } else { - $userlanguage = DEFAULT_LANGUAGE; - } - - debug_log('User language: ' . $userlanguage); - define('USERLANGUAGE', $userlanguage); -} else { - // Set user language to language from config. - define('USERLANGUAGE', $config->LANGUAGE_PRIVATE); -} diff --git a/core/bot/version.php b/core/bot/version.php index 9797e7a9..aa251da3 100644 --- a/core/bot/version.php +++ b/core/bot/version.php @@ -1,26 +1,22 @@ VERSION) or $config->VERSION = '1'; -$current = $config->VERSION; +require_once(ROOT_PATH . '/logic/bot_upgrade_check.php'); // Get version from VERSION file. $lfile = ROOT_PATH . '/VERSION'; -if(is_file($lfile) && filesize($lfile)) { - $latest = file($lfile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); - $latest = $latest[0]; -} else { +if(!is_file($lfile) || !filesize($lfile)) { $error = 'VERSION file missing, cannot continue without it since we would not know the required DB schema version.'; throw new Exception($error); } +$latest = file($lfile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); +$latest = $latest[0]; -// Current version not defined in config! -if($current == '1') { - $error = "Failed to determine your bot version! Have you removed it from config.json? or not defined POKEMONRAIDBOT_VERSION ? If this is a new installation, use the value {$latest}"; +// Check if version is defined in config. +if(!isset($config->VERSION) or empty($config->VERSION) or $config->VERSION == '1') { + $error = 'Failed to determine your bot version! Have you removed it from config.json? or not defined POKEMONRAIDBOT_VERSION ? If this is a new installation, use the value ' . $latest; throw new Exception($error); } +$current = $config->VERSION; // Check if we have the latest version and perform upgrade if needed & possible. bot_upgrade_check($current, $latest); diff --git a/core/commands/get.php b/core/commands/get.php deleted file mode 100644 index 473c8b6b..00000000 --- a/core/commands/get.php +++ /dev/null @@ -1,75 +0,0 @@ -ALLOWED_TELEGRAM_CONFIG); -$msg = '' . getTranslation('config') . ':' . CR . CR; - -// Get config restrictions for boolean input -$allowed_bool = explode(',', $config->ALLOW_ONLY_TRUE_FALSE); - -// Get config restrictions for numeric input -$allowed_numbers = explode(',', $config->ALLOW_ONLY_NUMBERS); - -// Get config. -$cfile = CONFIG_PATH . '/config.json'; -if(is_file($cfile)) { - $str = file_get_contents($cfile); - $json = json_decode($str, true); -} - -// Get config aliases. -$afile = CONFIG_PATH . '/alias.json'; -if(is_file($afile)) { - $astr = file_get_contents($afile); - $ajson = json_decode($astr, true); -} - -// Write to log. -debug_log('User requested the allowed telegram configs'); -debug_log('Allowed telegram configs: ' . $config->ALLOWED_TELEGRAM_CONFIG); -debug_log('Allow only boolean input: ' . $config->ALLOW_ONLY_TRUE_FALSE); -debug_log('Allow only numeric input: ' . $config->ALLOW_ONLY_NUMBERS); - -// Any configs allowed? -if(!empty($config->ALLOWED_TELEGRAM_CONFIG)) { - foreach($json as $cfg_name => $cfg_value) { - // Only allowed configs - if(in_array($cfg_name, $allowed)) { - // Is alias set? - $alias = ''; - if(isset($ajson[$cfg_name])){ - $alias = $ajson[$cfg_name]; - } - // Config name / Alias + value - $msg .= (empty($alias) ? $cfg_name : $alias) . SP . (empty($cfg_value) ? '' . getTranslation('no_value') . '' : $cfg_value); - - // Only bool? - if(in_array($cfg_name, $allowed_bool)) { - $msg .= SP . '(' . getTranslation('help_only_bool') . ')' . CR; - - // Only numbers? - } else if(in_array($cfg_name, $allowed_numbers)) { - $msg .= SP . '(' . getTranslation('help_only_numbers') . ')' . CR; - - // Any type - } else { - $msg .= CR; - } - } - } -} else { - $msg .= getTranslation('not_supported'); -} - -send_message($update['message']['chat']['id'], $msg); - -?> diff --git a/core/commands/set.php b/core/commands/set.php deleted file mode 100644 index 693ff218..00000000 --- a/core/commands/set.php +++ /dev/null @@ -1,182 +0,0 @@ -ALLOWED_TELEGRAM_CONFIG); - -// Get config restrictions for boolean input -$allowed_bool = explode(',', $config->ALLOW_ONLY_TRUE_FALSE); - -// Get config restrictions for numeric input -$allowed_numbers = explode(',', $config->ALLOW_ONLY_NUMBERS); - -// Write to log. -debug_log('User submitted a telegram config change'); -debug_log('Allowed telegram configs: ' . $config->ALLOWED_TELEGRAM_CONFIG); -debug_log('Allow only boolean input: ' . $config->ALLOW_ONLY_TRUE_FALSE); -debug_log('Allow only numeric input: ' . $config->ALLOW_ONLY_NUMBERS); - -// 0 means we reset config option value to "" -if($count == 0) { - // Upper input. - $config_name = strtoupper($input); - //$config_value = '"" (' . getTranslation('no_value') . ' / ' . getTranslation('resetted') . ')'; - $config_value = ""; - debug_log('Reset for the config value ' . $config_name . ' was requested by the user'); - -// 1 means we set the config option to the given value -} else if($count >= 1) { - // Config name and value. - $cfg_name_value = explode(' ', $input, 2); - $config_name = strtoupper($cfg_name_value[0]); - $config_value = $cfg_name_value[1]; - debug_log('Change for the config option ' . $config_name . ' was requested by the user'); - -// Set config_name to avoid undefined variable for if clause below. -} else { - $config_name = 'not_supported'; -} - -// Config -$cfile = CONFIG_PATH . '/config.json'; -if(is_file($cfile)) { - $str = file_get_contents($cfile); - $json = json_decode($str, true); -} - -// Real config name or alias? -$alias = ''; -$afile = CONFIG_PATH . '/alias.json'; -if(is_file($afile)) { - debug_log('Checking alias for config option ' . $config_name); - $astr = file_get_contents($afile); - // We compare always uppercase, so change str to upper - $astr = strtoupper($astr); - $ajson = json_decode($astr, true); - $alias = array_search($config_name, $ajson); - // Check for alias - if ($alias !== false) { - debug_log('Config option ' . $config_name . ' is an alias for ' . $alias); - $help = $config_name; - $config_name = strtoupper($alias); - $alias = $help; - } else { - debug_log('No alias found. Seems ' . $config_name . ' is the config option name'); - } -} - -// Assume restrictions. -$restrict = 'yes'; - -// Init additional error info. -$msg_error_info = ''; - -// Make sure it's allowed to update the value via telegram. -if(in_array($config_name, $allowed)) { - // Only bool? - if(in_array($config_name, $allowed_bool)) { - if(strcasecmp($config_value, true) == 0 || strcasecmp($config_value, false) == 0) { - $config_value = strtolower($config_value); - $restrict = 'no'; - } else if($config_value == 0 || $config_value == 1) { - $restrict = 'no'; - } else { - debug_log('Boolean value expected. Got this value: ' . $config_value); - $msg_error_info .= getTranslation('help_bool_expected'); - } - - - // Only numbers? - } else if(in_array($config_name, $allowed_numbers)) { - if(is_numeric($config_value)) { - $restrict = 'no'; - } else { - debug_log('Number expected. Got this value: ' . $config_value); - $msg_error_info .= getTranslation('help_number_expected'); - } - - // No restriction on input type. - } else { - $restrict = 'no'; - } -} - -// Update config. -if(in_array($config_name, $allowed) && $restrict == 'no') { - // Prepare data, replace " with ' - $config_value = str_replace('"', "'", $config_value); - $old_value = $json[$config_name]; - $json[$config_name] = $config_value; - $json = json_encode($json, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK | JSON_PRETTY_PRINT); - debug_log($config_name, 'CONFIG NAME:'); - debug_log($config_value, 'CONFIG VALUE:'); - - // Write to file. - if(!(is_array($json) && is_string(json_decode($json, true)) && (json_last_error() === JSON_ERROR_NONE))) { - file_put_contents(CONFIG_PATH . '/config.json', $json); - $msg = getTranslation('config_updated') . ':' . CR . CR; - $msg .= '' . (empty($alias) ? $config_name : $alias) . '' . CR; - $msg .= getTranslation('old_value') . SP . $old_value . CR; - $msg .= getTranslation('new_value') . SP . $config_value . CR; - debug_log('Changed the config value for ' . $config_name . ' from ' . $old_value . ' to ' . $config_value); - } else { - $msg_error_info = getTranslation('internal_error'); - $msg = '' . getTranslation('invalid_input') . '' . (!empty($msg_error_info) ? (CR . $msg_error_info) : '') . CR . CR; - } - -// Tell user how to set config and what is allowed to be set by config. -} else { - $msg = '' . getTranslation('invalid_input') . '' . (!empty($msg_error_info) ? (CR . $msg_error_info) : '') . CR . CR; - $msg .= '' . getTranslation('config') . ':' . CR; - // Any configs allowed? - if(!empty($config->ALLOWED_TELEGRAM_CONFIG)) { - $msg .= '/setconfig' . SP . getTranslation('option_value') . '' . CR; - foreach($json as $cfg_name => $cfg_value) { - // Only allowed configs - if(in_array($cfg_name, $allowed)) { - // Is alias set? - $alias = ''; - if(isset($ajson[$cfg_name])){ - $alias = $ajson[$cfg_name]; - } - // Config name / Alias + value - $msg .= '/set' . SP . (empty($alias) ? $cfg_name : $alias) . SP . (empty($cfg_value) ? '' . getTranslation('no_value') . '' : $cfg_value); - - // Only bool? - if(in_array($cfg_name, $allowed_bool)) { - $msg .= SP . '(' . getTranslation('help_only_bool') . ')' . CR; - - // Only numbers? - } else if(in_array($cfg_name, $allowed_numbers)) { - $msg .= SP . '(' . getTranslation('help_only_numbers') . ')' . CR; - - // Any type - } else { - $msg .= CR; - } - } - } - } else { - $msg .= getTranslation('not_supported'); - } - debug_log('Unsupported request for a telegram config change: ' . $input); -} - -// Send message. -send_message($update['message']['chat']['id'], $msg); - -?> diff --git a/core/lang/help.json b/core/lang/help.json deleted file mode 100644 index 55218974..00000000 --- a/core/lang/help.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "help_config-get": { - "NL": "/get - laat bot configuratie zien", - "DE": "/get - Bot-Konfiguration anzeigen", - "EN": "/get - Show bot configuration", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "/get - Näytä botin asetukset", - "ES": "/get - Ver configuración del bot" - }, - "help_config-set": { - "NL": "/set - Verander bot configuratie", - "DE": "/set - Bot-Konfiguration bearbeiten", - "EN": "/set - Edit bot configuration", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "/set - Muokkaa botin asetuksia", - "ES": "/set - Editar configuración del bot" - }, - "help_only_bool": { - "NL": "Alleen true/false of 0/1", - "DE": "nur true/false oder 0/1", - "EN": "only true/false or 0/1", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "vain true/false tai 0/1", - "ES": "solo true/false o 0/1" - }, - "help_only_numbers": { - "NL": "Alleen nummers", - "DE": "nur Zahlen", - "EN": "only numbers", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "vain numeroita", - "ES": "solo números" - }, - "help_bool_expected": { - "NL": "Toegestaande waarde: true/false of 0/1", - "DE": "Zulässige Werte: true/false oder 0/1", - "EN": "Allowed values: true/false or 0/1", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Sallitut arvot: true/false tai 0/1", - "ES": "Valores validos: true/false o 0/1" - }, - "help_number_expected": { - "NL": "Toegestaande waarde: Nummers groter dan 0", - "DE": "Zulässiger Wert: Zahlen größer 0", - "EN": "Allowed value: Numbers greater than 0", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Sallittu arvo: 0 suuremmat luvut", - "ES": "Valores validos: Números mayores que 0" - } -} diff --git a/core/lang/language.json b/core/lang/language.json deleted file mode 100644 index 153b90f3..00000000 --- a/core/lang/language.json +++ /dev/null @@ -1,782 +0,0 @@ -{ - "lang_name": { - "NL": "Nederlands", - "DE": "Deutsch", - "EN": "English", - "IT": "Italiano", - "PT-BR": "Português", - "RU": "Русский", - "NO": "Norsk", - "FR": "Français", - "PL": "Polski", - "FI": "Suomi", - "ES": "Español" - }, - "weekday_1": { - "NL": "Maandag", - "DE": "Montag", - "EN": "Monday", - "IT": "Lunedì", - "PT-BR": "segunda-feira", - "RU": "Понедельник", - "NO": "Mandag", - "FR": "Lundi", - "PL": "Poniedziałek", - "FI": "Maanantai", - "ES": "Lunes" - }, - "weekday_2": { - "NL": "Dinsdag", - "DE": "Dienstag", - "EN": "Tuesday", - "IT": "Martedì", - "PT-BR": "terça-feira", - "RU": "Вторник", - "NO": "Tirsdag", - "FR": "Mardi", - "PL": "Wtorek", - "FI": "Tiistai", - "ES": "Martes" - }, - "weekday_3": { - "NL": "Woensdag", - "DE": "Mittwoch", - "EN": "Wednesday", - "IT": "Mercoledì", - "PT-BR": "quarta-feira", - "RU": "Среда", - "NO": "Onsdag", - "FR": "Mercredi", - "PL": "Środa", - "FI": "Keskiviikko", - "ES": "Miércoles" - }, - "weekday_4": { - "NL": "Donderdag", - "DE": "Donnerstag", - "EN": "Thursday", - "IT": "Giovedì", - "PT-BR": "quinta-feira", - "RU": "Четверг", - "NO": "Torsdag", - "FR": "Jeudi", - "PL": "Czwartek", - "FI": "Torstai", - "ES": "Jueves" - }, - "weekday_5": { - "NL": "Vrijdag", - "DE": "Freitag", - "EN": "Friday", - "IT": "Venerdì", - "PT-BR": "sexta-feira", - "RU": "Пятница", - "NO": "Fredag", - "FR": "Vendredi", - "PL": "Piątek", - "FI": "Perjantai", - "ES": "Viernes" - }, - "weekday_6": { - "NL": "Zaterdag", - "DE": "Samstag", - "EN": "Saturday", - "IT": "Sabato", - "PT-BR": "sábado", - "RU": "Суббота", - "NO": "Lørdag", - "FR": "Samedi", - "PL": "Sobota", - "FI": "Lauantai", - "ES": "Sábado" - }, - "weekday_7": { - "NL": "Zondag", - "DE": "Sonntag", - "EN": "Sunday", - "IT": "Domenica", - "PT-BR": "domingo", - "RU": "Воскресение", - "NO": "Søndag", - "FR": "Dimanche", - "PL": "Niedziela", - "FI": "Sunnuntai", - "ES": "Domingo" - }, - "month_01": { - "NL": "Januari", - "DE": "Januar", - "EN": "January", - "IT": "Gennaio", - "PT-BR": "Janeiro", - "RU": "Январь", - "NO": "Januar", - "FR": "Janvier", - "PL": "Styczeń", - "FI": "Tammikuu", - "ES": "Enero" - }, - "month_02": { - "NL": "Februari", - "DE": "Februar", - "EN": "February", - "IT": "Febbraio", - "PT-BR": "Fevereiro", - "RU": "Февраль", - "NO": "Februar", - "FR": "Février", - "PL": "Luty", - "FI": "Helmikuu", - "ES": "Febrero" - }, - "month_03": { - "NL": "Maart", - "DE": "März", - "EN": "March", - "IT": "Marzo", - "PT-BR": "Março", - "RU": "Март", - "NO": "Mars", - "FR": "Mars", - "PL": "Marzec", - "FI": "Maaliskuu", - "ES": "Marzo" - }, - "month_04": { - "NL": "April", - "DE": "April", - "EN": "April", - "IT": "Aprile", - "PT-BR": "Abril", - "RU": "Апрель", - "NO": "April", - "FR": "Avril", - "PL": "Kwiecień", - "FI": "Huhtikuu", - "ES": "Abril" - }, - "month_05": { - "NL": "Mei", - "DE": "Mai", - "EN": "May", - "IT": "Maggio", - "PT-BR": "Maio", - "RU": "Май", - "NO": "Mai", - "FR": "Mai", - "PL": "Maj", - "FI": "Toukokuu", - "ES": "Mayo" - }, - "month_06": { - "NL": "Juni", - "DE": "Juni", - "EN": "June", - "IT": "Giugno", - "PT-BR": "Junho", - "RU": "Июнь", - "NO": "Juni", - "FR": "Juin", - "PL": "Czerwiec", - "FI": "Kesäkuu", - "ES": "Junio" - }, - "month_07": { - "NL": "Juli", - "DE": "Juli", - "EN": "July", - "IT": "Luglio", - "PT-BR": "Julho", - "RU": "Июль", - "NO": "Juli", - "FR": "Juillet", - "PL": "Lipiec", - "FI": "Heinäkuu", - "ES": "Julio" - }, - "month_08": { - "NL": "Augustus", - "DE": "August", - "EN": "August", - "IT": "Agosto", - "PT-BR": "Agosto", - "RU": "Август", - "NO": "August", - "FR": "Août", - "PL": "Sierpień", - "FI": "Elokuu", - "ES": "Agosto" - }, - "month_09": { - "NL": "September", - "DE": "September", - "EN": "September", - "IT": "Settembre", - "PT-BR": "Setembro", - "RU": "Сентябрь", - "NO": "September", - "FR": "Septembre", - "PL": "Wrzesień", - "FI": "Syyskuu", - "ES": "Septiembre" - }, - "month_10": { - "NL": "Oktober", - "DE": "Oktober", - "EN": "October", - "IT": "Ottobre", - "PT-BR": "Outubro", - "RU": "Октябрь", - "NO": "Oktober", - "FR": "Octobre", - "PL": "Październik", - "FI": "Lokakuu", - "ES": "Octubre" - }, - "month_11": { - "NL": "November", - "DE": "November", - "EN": "November", - "IT": "Novembre", - "PT-BR": "Novembro", - "RU": "Ноябрь", - "NO": "November", - "FR": "Novembre", - "PL": "Listopad", - "FI": "Marraskuu", - "ES": "Noviembre" - }, - "month_12": { - "NL": "December", - "DE": "Dezember", - "EN": "December", - "IT": "Dicembre", - "PT-BR": "Dezembro", - "RU": "Декабрь", - "NO": "Desember", - "FR": "Décembre", - "PL": "Grudzień", - "FI": "Joulukuu", - "ES": "Diciembre" - }, - "bot_access_denied": { - "NL": "Je hebt geen toegang tot deze functie of bot!", - "DE": "Sie haben keine Berechtigung diesen Befehl oder Bot zu nutzen!", - "EN": "You are not allowed to use this command or bot!", - "IT": "Non hai i permessi necessari per usare questo comando o bot!", - "PT-BR": "Você não tem permissão para utilizar esse comando ou bot!", - "RU": "Вы не можете использовать эту команду или бота!", - "NO": "Du har ikke tillatelse til å bruke denne kommandoen eller boten!", - "FR": "Tu n'es pas autorisé à utiliser cette commande !", - "PL": "Nie masz uprawnień do korzystania z tej komendy lub bota!", - "FI": "Sinulla ei ole oikeutta tähän komentoon tai bottiin!", - "ES": "¡No está autorizado a utilizar este comando o bot!" - }, - "forest": { - "NL": "Routebeschrijving", - "DE": "Wegbeschreibung", - "EN": "Directions", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Reittiohje", - "ES": "Abrir mapa" - }, - "pokemon": { - "NL": "Pokemon", - "DE": "Pokemon", - "EN": "Pokemon", - "IT": "Pokemon", - "PT-BR": "Pokemon", - "RU": "Pokemon", - "NO": "Pokemon", - "FR": "Pokemon", - "PL": "Pokemon", - "FI": "Pokemon", - "ES": "Pokémon" - }, - "pokemon_types": { - "NL": "Pokemon Types", - "DE": "Pokemon-Typen", - "EN": "Pokemon Types", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Pokemon Tyypit", - "ES": "Tipos de Pokémon" - }, - "yes": { - "NL": "Ja", - "DE": "Ja", - "EN": "Yes", - "IT": "Sì", - "PT-BR": "Sim", - "RU": "Да", - "NO": "Ja", - "FR": "Oui", - "PL": "Tak", - "FI": "Kyllä", - "ES": "Si" - }, - "no": { - "NL": "Nee", - "DE": "Nein", - "EN": "No", - "IT": "No", - "PT-BR": "Não", - "RU": "Нет", - "NO": "Nei", - "FR": "Non", - "PL": "Nie", - "FI": "Ei", - "ES": "No" - }, - "or": { - "NL": "of", - "DE": "oder", - "EN": "or", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "tai", - "ES": "o" - }, - "not_supported": { - "NL": "Niet ondersteund", - "DE": "Nicht unterstützt", - "EN": "Not supported", - "IT": "Non compatibile", - "PT-BR": "Não compatível", - "RU": "Не поддерживается", - "NO": "Ikke støttet", - "FR": "Non compatible", - "PL": "Nieobsługiwane", - "FI": "Ei tuettu", - "ES": "No soportado" - }, - "abort": { - "NL": "Afbreken", - "DE": "Abbrechen", - "EN": "Abort", - "IT": "Cancella", - "PT-BR": "Abortar", - "RU": "Прервать", - "NO": "Avbryt", - "FR": "Abandonner", - "PL": "Anuluj", - "FI": "Peruuta", - "ES": "Cancelar" - }, - "action_aborted": { - "NL": "Het proces is afgebroken!", - "DE": "Der Vorgang wurde abgebrochen!", - "EN": "The process was aborted!", - "IT": "Il processo è stato cancellato!", - "PT-BR": "O processo foi abortado!", - "RU": "Процесс был прерван!", - "NO": "Handlingen ble avbrutt!", - "FR": "Le processus à échoué !", - "PL": "Zadanie zostało anulowane", - "FI": "Prosessi peruutettiin!", - "ES": "¡El proceso fue cancelado!" - }, - "select_action": { - "NL": "Selecteer actie:", - "DE": "Aktion auswählen:", - "EN": "Select action:", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Valitse toiminto:", - "ES": "Selecciona la acción:" - }, - "back": { - "NL": "Terug", - "DE": "Zurück", - "EN": "Back", - "IT": "Precedente", - "PT-BR": "Voltar", - "RU": "Назад", - "NO": "Tilbake", - "FR": "Retour", - "PL": "Wstecz", - "FI": "Takaisin", - "ES": "Volver" - }, - "next": { - "NL": "Volgende", - "DE": "Weiter", - "EN": "Next", - "IT": "Successivo", - "PT-BR": "Próximo", - "RU": "Вперед", - "NO": "Neste", - "FR": "Suivant", - "PL": "Dalej", - "FI": "Seuraava", - "ES": "Siguiente" - }, - "done": { - "NL": "Klaar", - "DE": "Fertig", - "EN": "Done", - "IT": "Completato", - "PT-BR": "Feito", - "RU": "Готово", - "NO": "Ferdig", - "FR": "Terminé", - "PL": "Zrobione", - "FI": "Valmis", - "ES": "Hecho" - }, - "add": { - "NL": "Toevoegen", - "DE": "Hinzufügen", - "EN": "Add", - "IT": "Aggiungi", - "PT-BR": "Adicionar", - "RU": "Добавить", - "NO": "Legg til", - "FR": "Ajouter", - "PL": "Dodaj", - "FI": "Lisää", - "ES": "Añadir" - }, - "replace": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Replace", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Korvaa", - "ES": "Reemplazar" - }, - "delete": { - "NL": "Verwijder", - "DE": "Löschen", - "EN": "Delete", - "IT": "Elimina", - "PT-BR": "Deletar", - "RU": "Удалить", - "NO": "Slett", - "FR": "Supprimer", - "PL": "Usuń", - "FI": "Poista", - "ES": "Borrar" - }, - "edit": { - "NL": "Bewerken", - "DE": "Bearbeiten", - "EN": "Edit", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Muokkaa", - "ES": "Editar" - }, - "list": { - "NL": "Lijst", - "DE": "Anzeigen", - "EN": "List", - "IT": "Lista", - "PT-BR": "Lista", - "RU": "Список", - "NO": "List", - "FR": "Liste", - "PL": "Pokaż", - "FI": "Listaa", - "ES": "Lista" - }, - "reset": { - "NL": "Reset", - "DE": "Reset", - "EN": "Reset", - "IT": "Reset", - "PT-BR": "Resetar", - "RU": "Сброс", - "NO": "Reset", - "FR": "Remise à zéro", - "PL": "Resetuj", - "FI": "Resetoi", - "ES": "Reiniciar" - }, - "created_by": { - "NL": "Aangemaakt door", - "DE": "Erstellt von", - "EN": "Created by", - "IT": "Creato da", - "PT-BR": "Criada por", - "RU": "Создано", - "NO": "Postet av", - "FR": "Créé par", - "PL": "Stworzone przez", - "FI": "Luonut", - "ES": "Creado por" - }, - "updated": { - "NL": "Bijgewerkt", - "DE": "Aktualisiert", - "EN": "Updated", - "IT": "Aggiornato", - "PT-BR": "Atualizada", - "RU": "Обновлено", - "NO": "Oppdatert", - "FR": "Mis à jour", - "PL": "Zaktualizowano", - "FI": "Päivitetty", - "ES": "Actualizado" - }, - "share": { - "NL": "Delen", - "DE": "Teilen", - "EN": "Share", - "IT": "Condividi", - "PT-BR": "Compartilhar", - "RU": "Поделиться", - "NO": "Del", - "FR": "Partager", - "PL": "Udostępnij", - "FI": "Jaa", - "ES": "Compartir" - }, - "share_with": { - "NL": "Delen met", - "DE": "Teilen mit", - "EN": "Share with", - "IT": "Condividi con", - "PT-BR": "Compartilhar com", - "RU": "Поделиться с", - "NO": "Del med", - "FR": "Partager avec", - "PL": "Przekaż dalej", - "FI": "Jaa ryhmään", - "ES": "Compartir con" - }, - "successfully_shared": { - "NL": "Succesvol gedeeld!", - "DE": "Erfolgreich geteilt!", - "EN": "Successfully shared!", - "IT": "Condiviso con successo!", - "PT-BR": "Compartilhado com sucesso!", - "RU": "Успешно отправлено!", - "NO": "Deling velykket!", - "FR": "Partagé avec succès !", - "PL": "Pomyślnie udostępniono", - "FI": "Jaettu onnistuneesti!", - "ES": "¡Compartido con éxito!" - }, - "config": { - "NL": "Configuratie", - "DE": "Konfiguration", - "EN": "Configuration", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Asetukset", - "ES": "Configuración" - }, - "config_updated": { - "NL": "Configuratie ge-update", - "DE": "Konfiguration aktualisiert", - "EN": "Configuration updated", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Asetukset päivitetty", - "ES": "Configuración actualizada" - }, - "resetted": { - "NL": "Gereset", - "DE": "Zurückgesetzt", - "EN": "Resetted", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Resetoitu", - "ES": "Reiniciado" - }, - "option_value": { - "NL": "Configuratie optie waarde", - "DE": "Konfigurationsoption Wert", - "EN": "Configurationoption value", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Asetuksen arvo", - "ES": "Valor de la opción de configuración" - }, - "no_value": { - "NL": "Geen waarde", - "DE": "Kein Wert", - "EN": "No value", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Ei arvoa", - "ES": "Sin valor" - }, - "new_value": { - "NL": "Nieuwe waarde:", - "DE": "Neuer Wert:", - "EN": "New value:", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Uusi arvo:", - "ES": "Nuevo valor:" - }, - "old_value": { - "NL": "Oude waarde:", - "DE": "Alter Wert:", - "EN": "Old value:", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Vanha arvo:", - "ES": "Antiguo valor:" - }, - "personal_help": { - "NL": "Alle commando's voor de bot:", - "DE": "Deine persönliche Bot-Hilfe:", - "EN": "Your personal bot help:", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Henkilökohtainen bottiapu:", - "ES": "Tu bot personal ayuda:" - }, - "invalid_input": { - "NL": "Ongeldige input!", - "DE": "Ungültige Eingabe!", - "EN": "Invalid input!", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Epäkelpo arvo!", - "ES": "¡Entrada inválida!" - }, - "internal_error": { - "NL": "Internal error!", - "DE": "Interner Fehler!", - "EN": "Internal error!", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Sisäinen virhe!", - "ES": "¡Error interno!" - }, - "expand": { - "NL": "Uitklappen", - "DE": "Ausklappen", - "EN": "Expand", - "IT": "Espandi", - "PT-BR": "Expandir", - "RU": "Развернуть", - "NO": "Flere valg", - "FR": "Agrandir", - "PL": "Rozwiń", - "FI": "Laajenna", - "ES": "Expandir" - }, - "none": { - "NL": "Geen", - "DE": "Keine", - "EN": "None", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "ei mitään", - "ES": "Nada" - }, - "other": { - "NL": "Anders", - "DE": "Sonstiges", - "EN": "Other", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Muu", - "ES": "Otro" - }, - "tutorial_vote_failed": { - "NL": "Je kan niet meedoen voordat je de tutorial hebt doorlopen.", - "DE": "TRANSLATE", - "EN": "You can't vote before going through the tutorial.", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Et voi äänestää ennen kuin olet käynyt ohjeen läpi.", - "ES": "No puedes votar antes de seguir el tutorial." - }, - "tutorial_command_failed": { - "NL": "Je kan dit commando niet gebruiken voordat je de tutorial hebt doorlopen.", - "DE": "TRANSLATE", - "EN": "You can't use this command before going through the tutorial.", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Et voi käyttää tätä käskyä ennen kuin olet käynyt ohjeen läpi.", - "ES": "No puede usar este comando antes de seguir el tutorial." - } -} diff --git a/core/lang/pokemon_forms.json b/core/lang/pokemon_forms.json deleted file mode 100644 index a44c8157..00000000 --- a/core/lang/pokemon_forms.json +++ /dev/null @@ -1,829 +0,0 @@ -{ - "pokemon_form_alola": { - "NL": "Alolan", - "DE": "Alola", - "EN": "Alolan", - "IT": "TRANSLATE", - "PT-BR": "de Alola", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Alolan", - "FI": "Alolan", - "ES": "Alola" - }, - "pokemon_form_galarian": { - "NL": "Galarian", - "DE": "Galar", - "EN": "Galarian", - "IT": "Galarian", - "PT-BR": "Galarian", - "RU": "Galarian", - "NO": "Galarian", - "FR": "Galarian", - "PL": "Galarian", - "FI": "Galarian", - "ES": "Galar" - }, - "pokemon_form_hisuian": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Hisuian", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Hisuian", - "ES": "TRANSLATE" - }, - "pokemon_form_mega": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Mega", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Mega", - "ES": "Mega" - }, - "pokemon_form_mega_x": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Mega X", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Mega X", - "ES": "Mega X" - }, - "pokemon_form_mega_y": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Mega Y", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Mega Y", - "ES": "Mega Y" - }, - "pokemon_form_armored": { - "NL": "Armored", - "DE": "Rüstungsform", - "EN": "Armored", - "IT": "Armatura", - "PT-BR": "Armadura", - "RU": "Bronya", - "NO": "Rustning", - "FR": "Armure", - "ES": "Acorazado" - }, - "pokemon_form_attack": { - "NL": "Attack", - "DE": "Angriffsform", - "EN": "Attack", - "IT": "TRANSLATE", - "PT-BR": "Ataque", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Attack", - "ES": "Ataque" - }, - "pokemon_form_defense": { - "NL": "Defense", - "DE": "Verteidigungsform", - "EN": "Defense", - "IT": "TRANSLATE", - "PT-BR": "Defesa", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Defense", - "ES": "Defensa" - }, - "pokemon_form_speed": { - "NL": "Speed", - "DE": "Initiativeform", - "EN": "Speed", - "IT": "TRANSLATE", - "PT-BR": "Velocidade", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Speed", - "ES": "Velocidad" - }, - "pokemon_form_plant": { - "NL": "Plant", - "DE": "Pflanzenumhang", - "EN": "Plant", - "IT": "TRANSLATE", - "PT-BR": "Planta", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Plant", - "ES": "Planta" - }, - "pokemon_form_sandy": { - "NL": "Sandy", - "DE": "Sandumhang", - "EN": "Sandy", - "IT": "TRANSLATE", - "PT-BR": "Areia", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Sandy", - "ES": "Arena" - }, - "pokemon_form_trash": { - "NL": "Trash", - "DE": "Lumpenumhang", - "EN": "Trash", - "IT": "TRANSLATE", - "PT-BR": "Lixo", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Trash", - "ES": "Basura" - }, - "pokemon_form_overcast": { - "NL": "Overcast", - "DE": "Wolkenform", - "EN": "Overcast", - "IT": "TRANSLATE", - "PT-BR": "Negativo", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Overcast", - "ES": "Encapotada" - }, - "pokemon_form_sunny": { - "NL": "Sunny", - "DE": "Sonnenform", - "EN": "Sunny", - "IT": "TRANSLATE", - "PT-BR": "Positivo", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Sunny", - "ES": "Soleada" - }, - "pokemon_form_rainy": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Rainy", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_snowy": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Snowy", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_east_sea": { - "NL": "East", - "DE": "Ostform", - "EN": "East", - "IT": "TRANSLATE", - "PT-BR": "Leste", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "East", - "ES": "Este" - }, - "pokemon_form_west_sea": { - "NL": "West", - "DE": "Westform", - "EN": "West", - "IT": "TRANSLATE", - "PT-BR": "Oeste", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "West", - "ES": "Oeste" - }, - "pokemon_form_fan": { - "NL": "Fan", - "DE": "Wirbel-Form", - "EN": "Fan", - "IT": "TRANSLATE", - "PT-BR": "Ventilador", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Fan", - "ES": "Ventilador" - }, - "pokemon_form_frost": { - "NL": "Frost", - "DE": "Frost-Form", - "EN": "Frost", - "IT": "TRANSLATE", - "PT-BR": "Congelado", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Frost", - "ES": "Frío" - }, - "pokemon_form_heat": { - "NL": "Heat", - "DE": "Hitze-Form", - "EN": "Heat", - "IT": "TRANSLATE", - "PT-BR": "Calor", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Heat", - "ES": "Calor" - }, - "pokemon_form_mow": { - "NL": "Mow", - "DE": "Schneid-Form", - "EN": "Mow", - "IT": "TRANSLATE", - "PT-BR": "Cortador", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Mow", - "ES": "Corte" - }, - "pokemon_form_wash": { - "NL": "Wash", - "DE": "Wasch-Form", - "EN": "Wash", - "IT": "TRANSLATE", - "PT-BR": "Lavagem", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Wash", - "ES": "Lavado" - }, - "pokemon_form_altered": { - "NL": "Altered", - "DE": "Wandelform", - "EN": "Altered", - "IT": "TRANSLATE", - "PT-BR": "Alterado", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Altered", - "ES": "Modificada" - }, - "pokemon_form_origin": { - "NL": "Origin", - "DE": "Urform", - "EN": "Origin", - "IT": "TRANSLATE", - "PT-BR": "Original", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Origin", - "ES": "Origen" - }, - "pokemon_form_land": { - "NL": "Land", - "DE": "Landform", - "EN": "Land", - "IT": "TRANSLATE", - "PT-BR": "Terrestre", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Land", - "ES": "Tierra" - }, - "pokemon_form_sky": { - "NL": "Sky", - "DE": "Zenitform", - "EN": "Sky", - "IT": "TRANSLATE", - "PT-BR": "Celeste", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Sky", - "ES": "Cielo" - }, - "pokemon_form_bug": { - "NL": "Bug", - "DE": "Käfer-Form", - "EN": "Bug", - "IT": "TRANSLATE", - "PT-BR": "Inseto", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Bug", - "ES": "Bicho" - }, - "pokemon_form_dark": { - "NL": "Dark", - "DE": "Unlicht-Form", - "EN": "Dark", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Dark", - "ES": "Siniestro" - }, - "pokemon_form_dragon": { - "NL": "Dragon", - "DE": "Drache-Form", - "EN": "Dragon", - "IT": "TRANSLATE", - "PT-BR": "Dragão", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Dragon", - "ES": "Dragón" - }, - "pokemon_form_electric": { - "NL": "Electric", - "DE": "Elektro-Form", - "EN": "Electric", - "IT": "TRANSLATE", - "PT-BR": "Elétrico", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Electric", - "ES": "Eléctrico" - }, - "pokemon_form_fairy": { - "NL": "Fairy", - "DE": "Fee-Form", - "EN": "Fairy", - "IT": "TRANSLATE", - "PT-BR": "Fada", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Fairy", - "ES": "Hada" - }, - "pokemon_form_flying": { - "NL": "Flying", - "DE": "Flug-Form", - "EN": "Flying", - "IT": "TRANSLATE", - "PT-BR": "Voador", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Flying", - "ES": "Volador" - }, - "pokemon_form_fighting": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Fighting", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_fire": { - "NL": "Fire", - "DE": "Feuer-Form", - "EN": "Fire", - "IT": "TRANSLATE", - "PT-BR": "Fogo", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Fire", - "ES": "Fuego" - }, - "pokemon_form_ghost": { - "NL": "Ghost", - "DE": "Geist-Form", - "EN": "Ghost", - "IT": "TRANSLATE", - "PT-BR": "Fantasma", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Ghost", - "ES": "Fantasma" - }, - "pokemon_form_grass": { - "NL": "Grass", - "DE": "Pflanze-Form", - "EN": "Grass", - "IT": "TRANSLATE", - "PT-BR": "Planta", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Grass", - "ES": "Planta" - }, - "pokemon_form_ground": { - "NL": "Ground", - "DE": "Boden-Form", - "EN": "Ground", - "IT": "TRANSLATE", - "PT-BR": "Terra", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Ground", - "ES": "Tierra" - }, - "pokemon_form_ice": { - "NL": "Ice", - "DE": "Eis-Form", - "EN": "Ice", - "IT": "TRANSLATE", - "PT-BR": "Gelo", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Ice", - "ES": "Hielo" - }, - "pokemon_form_poison": { - "NL": "Poison", - "DE": "Gift-Form", - "EN": "Poison", - "IT": "TRANSLATE", - "PT-BR": "Veneno", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Poison", - "ES": "Veneno" - }, - "pokemon_form_psychic": { - "NL": "Psychic", - "DE": "Psycho-Form", - "EN": "Psychic", - "IT": "TRANSLATE", - "PT-BR": "Psíquico", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Psychic", - "ES": "Psíquico" - }, - "pokemon_form_rock": { - "NL": "Rock", - "DE": "Gestein-Form", - "EN": "Rock", - "IT": "TRANSLATE", - "PT-BR": "Pedra", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Rock", - "ES": "Roca" - }, - "pokemon_form_steel": { - "NL": "Steel", - "DE": "Stahl-Form", - "EN": "Steel", - "IT": "TRANSLATE", - "PT-BR": "Metálico", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Steel", - "ES": "Acero" - }, - "pokemon_form_water": { - "NL": "Water", - "DE": "Wasser-Form", - "EN": "Water", - "IT": "TRANSLATE", - "PT-BR": "Água", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Water", - "ES": "Agua" - }, - "pokemon_form_incarnate": { - "NL": "Incarnate", - "DE": "Inkarnationsform", - "EN": "Incarnate", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Incarnate", - "ES": "Avatar" - }, - "pokemon_form_therian": { - "NL": "Therian", - "DE": "Tiergeistform", - "EN": "Therian", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Therian", - "ES": "Tótem" - }, - "pokemon_form_ordinary": { - "NL": "Ordinary", - "DE": "Standardform", - "EN": "Ordinary", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Ordinary", - "ES": "Normal" - }, - "pokemon_form_resolute": { - "NL": "Resolute", - "DE": "Resolutform", - "EN": "Resolute", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "Resolute", - "ES": "Brío" - }, - "pokemon_form_burn": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Burn", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_chill": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Chill", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_douse": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Douse", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_shock": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Shock", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_black": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Black", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_white": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "White", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_hero": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Hero", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_crowned_sword": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Crowned Sword", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_crowned_shield": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Crowned Shield", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_zen": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Zen", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_galarian_zen": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Galarian Zen", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_male": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Male", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_female": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Female", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_blue_striped": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Blue Striped", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_red_striped": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Red Striped", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_autumn": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Autumn", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_spring": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Spring", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_summer": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Summer", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_winter": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", - "EN": "Winter", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "ES": "TRANSLATE" - }, - "pokemon_form_exclamation_point": { - "EN": "!" - }, - "pokemon_form_question_mark": { - "EN": "?" - } -} diff --git a/core/lang/requirements.txt b/core/lang/requirements.txt deleted file mode 100644 index b998a06a..00000000 --- a/core/lang/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -absl-py diff --git a/core/lang/translate.py b/core/lang/translate.py deleted file mode 100644 index 465b3e98..00000000 --- a/core/lang/translate.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python3 - -from absl import flags, app, logging -import json - -FLAGS = flags.FLAGS - -flags.DEFINE_string('from_language', 'EN', 'Language to use as translation hint') -flags.DEFINE_string('to', None, 'Language to translate into') -flags.DEFINE_string('input', 'language.json', 'File to read base translation from.') -flags.DEFINE_string('output', 'language.json', 'File to keep saving changes to.') -flags.DEFINE_boolean('incremental', True, 'Update only strings missing translations.') -flags.DEFINE_boolean('sort_keys', False, 'Whether to output sorted or not.') -flags.mark_flag_as_required('to') - - -def main(argv): - with open(FLAGS.input, encoding='utf8') as f: - trans = json.load(f) - - logging.info('Creating placeholders for missing strings for language {0}'.format(FLAGS.to)) - for key in trans.keys(): - if FLAGS.to not in trans[key]: - trans[key][FLAGS.to] = 'TRANSLATE' - flush(trans) - print('Press ^D or ^C to stop. Leave a translation empty to skip.') - - - if FLAGS.incremental: - logging.info('Iterating over strings that have not been translated to language {0}'.format(FLAGS.to)) - for key in trans.keys(): - if trans[key][FLAGS.to] == 'TRANSLATE': - translate(trans, key) - flush(trans) - - else: - logging.info('Iterating over all strings of language {0}'.format(FLAGS.to)) - for key in trans.keys(): - translate(trans, key) - flush(trans) - -# Flush current changes to output file to avoid losing changes -def flush(trans): - logging.debug('flushing into {0}'.format(FLAGS.output)) - with open(FLAGS.output, 'w', encoding='utf8') as f: - # We dump without ASCII ensurance to get unicode output for example for Russian - json.dump(trans, f, indent=2, sort_keys=FLAGS.sort_keys, ensure_ascii=False) - -# Print from string, request to string and update it to trans -def translate(trans, key): - print('{0}[{1}]: {2}'.format(key, FLAGS.from_language, trans[key][FLAGS.from_language])) - translation = input('{0}[{1}]: '.format(key, FLAGS.to)) - if translation: - trans[key][FLAGS.to] = translation - - -if __name__ == '__main__': - app.run(main) diff --git a/core/telegram/functions.php b/core/telegram/functions.php index 6a10d0a0..88de4c5b 100644 --- a/core/telegram/functions.php +++ b/core/telegram/functions.php @@ -6,8 +6,8 @@ */ function is_valid_target($chat_id, $message_id, $no_chat = false, $no_message = false){ if($chat_id === null && isset($message_id)) { - debug_log("Inline message id received, skipping chat id check: {$message_id}"); - return true; + debug_log("Inline message id received, skipping chat id check: {$message_id}"); + return true; } debug_log("Checking for validity chat_id:{$chat_id} and message_id:{$message_id}"); // First check that both are numbers, if they are required @@ -24,60 +24,66 @@ function is_valid_target($chat_id, $message_id, $no_chat = false, $no_message = info_log("chat_id:{$chat_id}, message_id:{$message_id}", 'ERROR: Unhandled pair of chat_id & message_id, this is a bug:'); return true; } - +function create_chat_object($arr) { + $return = []; + $return['id'] = $arr[0] ?? 0; + $return['thread'] = $arr[1] ?? 0; + return $return; +} /** * Send message. - * @param int $chat_id - * @param array $text + * @param array $chatObj + * @param string $text * @param mixed $inline_keyboard - * @param array $merge_args - * @param array|false $multicurl + * @param array|bool $merge_args + * @param array|bool $multicurl * @param int|string $identifier * @return mixed */ -function send_message($chat_id, $text = [], $inline_keyboard = false, $merge_args = [], $multicurl = false, $identifier = false) +function send_message($chatObj, $text = [], $inline_keyboard = false, $merge_args = [], $multicurl = false, $identifier = false) { - // Create response content array. - $reply_content = [ - 'method' => 'sendMessage', - 'chat_id' => $chat_id, - 'parse_mode' => 'HTML', - 'text' => $text - ]; - if(!is_valid_target($chat_id, null, false, true)){ - info_log($chat_id, 'ERROR: Cannot send to invalid chat id:'); - info_log($reply_content, 'ERROR: data would have been:'); - exit(); - } + // Create response content array. + $reply_content = [ + 'method' => 'sendMessage', + 'chat_id' => $chatObj['id'], + 'parse_mode' => 'HTML', + 'text' => $text + ]; + if(isset($chatObj['thread'])) $reply_content['message_thread_id'] = $chatObj['thread']; + if(!is_valid_target($chatObj['id'], null, false, true)){ + info_log($chatObj['id'], 'ERROR: Cannot send to invalid chat id:'); + info_log($reply_content, 'ERROR: data would have been:'); + exit(); + } - // Write to log. - debug_log('KEYS'); - debug_log($inline_keyboard); + // Write to log. + debug_log('KEYS'); + debug_log($inline_keyboard); - if ($inline_keyboard != false) { - $reply_content['reply_markup'] = ['inline_keyboard' => $inline_keyboard]; - } + if ($inline_keyboard != false) { + $reply_content['reply_markup'] = ['inline_keyboard' => $inline_keyboard]; + } - if (is_array($merge_args) && count($merge_args)) { - $reply_content = array_merge_recursive($reply_content, $merge_args); - } + if (is_array($merge_args) && count($merge_args)) { + $reply_content = array_merge_recursive($reply_content, $merge_args); + } - // Encode data to json. - $reply_json = json_encode($reply_content); + // Encode data to json. + $reply_json = json_encode($reply_content); - // Set header to json. - header('Content-Type: application/json'); + // Set header to json. + header('Content-Type: application/json'); - // Write to log. - debug_log($reply_json, '>'); + // Write to log. + debug_log($reply_json, '>'); - // Send request to telegram api. - return curl_request($reply_json, $multicurl, $identifier); + // Send request to telegram api. + return curl_request($reply_json, $multicurl, $identifier); } /** * Send venue. - * @param $chat_id + * @param $chatObj * @param $lat * @param $lon * @param $title @@ -87,102 +93,105 @@ function send_message($chat_id, $text = [], $inline_keyboard = false, $merge_arg * @param $identifier * @return mixed */ -function send_venue($chat_id, $lat, $lon, $title, $address, $inline_keyboard = false, $multicurl = false, $identifier = false) +function send_venue($chatObj, $lat, $lon, $title, $address, $inline_keyboard = false, $multicurl = false, $identifier = false) { - // Create reply content array. - $reply_content = [ - 'method' => 'sendVenue', - 'chat_id' => $chat_id, - 'latitude' => $lat, - 'longitude' => $lon, - 'title' => $title, - 'address' => $address - ]; - if(!is_valid_target($chat_id, null, false, true)){ - info_log($chat_id, 'ERROR: Cannot send to invalid chat id:'); - info_log($reply_content, 'ERROR: data would have been:'); - exit(); - } + // Create reply content array. + $reply_content = [ + 'method' => 'sendVenue', + 'chat_id' => $chatObj['id'], + 'latitude' => $lat, + 'longitude' => $lon, + 'title' => $title, + 'address' => $address + ]; + if(isset($chatObj['thread'])) $reply_content['message_thread_id'] = $chatObj['thread']; + if(!is_valid_target($chatObj['id'], null, false, true)){ + info_log($chatObj['id'], 'ERROR: Cannot send to invalid chat id:'); + info_log($reply_content, 'ERROR: data would have been:'); + exit(); + } - // Write to log. - debug_log('KEYS'); - debug_log($inline_keyboard); + // Write to log. + debug_log('KEYS'); + debug_log($inline_keyboard); - if (is_array($inline_keyboard)) { - $reply_content['reply_markup'] = ['inline_keyboard' => $inline_keyboard]; - } + if (is_array($inline_keyboard)) { + $reply_content['reply_markup'] = ['inline_keyboard' => $inline_keyboard]; + } - // Encode data to json. - $reply_json = json_encode($reply_content); + // Encode data to json. + $reply_json = json_encode($reply_content); - // Set header to json. - header('Content-Type: application/json'); + // Set header to json. + header('Content-Type: application/json'); - // Write to log. - debug_log($reply_json, '>'); + // Write to log. + debug_log($reply_json, '>'); - // Send request to telegram api and return response. - return curl_request($reply_json, $multicurl, $identifier); + // Send request to telegram api and return response. + return curl_request($reply_json, $multicurl, $identifier); } /** * Echo message. - * @param $chat_id + * @param $chatObj * @param $text */ -function sendMessageEcho($chat_id, $text) +function sendMessageEcho($chatObj, $text) { - // Create reply content array. - $reply_content = [ - 'method' => 'sendMessage', - 'chat_id' => $chat_id, - 'parse_mode' => 'HTML', - 'text' => $text - ]; - if(!is_valid_target($chat_id, null, false, true)){ - info_log($chat_id, 'ERROR: Cannot send to invalid chat id:'); - info_log($reply_content, 'ERROR: data would have been:'); - exit(); - } + // Create reply content array. + $reply_content = [ + 'method' => 'sendMessage', + 'chat_id' => $chatObj['id'], + 'parse_mode' => 'HTML', + 'text' => $text + ]; + if(isset($chatObj['thread'])) $reply_content['message_thread_id'] = $chatObj['thread']; + if(!is_valid_target($chatObj['id'], null, false, true)){ + info_log($chatObj['id'], 'ERROR: Cannot send to invalid chat id:'); + info_log($reply_content, 'ERROR: data would have been:'); + exit(); + } - // Encode data to json. - $reply_json = json_encode($reply_content); + // Encode data to json. + $reply_json = json_encode($reply_content); - // Set header to json. - header('Content-Type: application/json'); + // Set header to json. + header('Content-Type: application/json'); - // Write to log. - debug_log($reply_json, '>'); + // Write to log. + debug_log($reply_json, '>'); - // Echo json. - echo($reply_json); + // Echo json. + echo($reply_json); } /** * Answer callback query. - * @param $query_id - * @param $text + * @param int $query_id + * @param string $text + * @param bool $multicurl */ function answerCallbackQuery($query_id, $text, $multicurl = false) { - // Create response array. - $response = [ - 'method' => 'answerCallbackQuery', - 'callback_query_id' => $query_id, - 'text' => $text - ]; + // Create response array. + $response = [ + 'method' => 'answerCallbackQuery', + 'callback_query_id' => $query_id, + 'text' => $text + ]; - // Encode response to json format. - $json_response = json_encode($response); + // Encode response to json format. + $json_response = json_encode($response); - // Set header to json. - header('Content-Type: application/json'); + // Set header to json. + header('Content-Type: application/json'); - // Write to log. - debug_log($json_response, '>'); + // Write to log. + debug_log($json_response, '>'); - // Send request to telegram api. - return curl_request($json_response, $multicurl); + // Send request to telegram api. + return curl_request($json_response, $multicurl); } /** @@ -192,50 +201,50 @@ function answerCallbackQuery($query_id, $text, $multicurl = false) */ function answerInlineQuery($query_id, $contents) { - // Init empty result array. - $results = []; - - // For each content. - foreach($contents as $key => $row) { - $text = $contents[$key]['text']; - $title = $contents[$key]['title']; - $desc = $contents[$key]['desc']; - $inline_keyboard = $contents[$key]['keyboard']; - - // Create input message content array. - $input_message_content = [ - 'parse_mode' => 'HTML', - 'message_text' => $text, - 'disable_web_page_preview' => true - ]; - - // Fill results array. - $results[] = [ - 'type' => 'article', - 'id' => $query_id . $key, - 'title' => $title, - 'description' => $desc, - 'input_message_content' => $input_message_content, - 'reply_markup' => [ - 'inline_keyboard' => $inline_keyboard - ] - ]; - } + // Init empty result array. + $results = []; + + // For each content. + foreach($contents as $key => $row) { + $text = $contents[$key]['text']; + $title = $contents[$key]['title']; + $desc = $contents[$key]['desc']; + $inline_keyboard = $contents[$key]['keyboard']; + + // Create input message content array. + $input_message_content = [ + 'parse_mode' => 'HTML', + 'message_text' => $text, + 'disable_web_page_preview' => true + ]; - // Create reply content array. - $reply_content = [ - 'method' => 'answerInlineQuery', - 'inline_query_id' => $query_id, - 'is_personal' => true, - 'cache_time' => 10, - 'results' => $results + // Fill results array. + $results[] = [ + 'type' => 'article', + 'id' => $query_id . $key, + 'title' => $title, + 'description' => $desc, + 'input_message_content' => $input_message_content, + 'reply_markup' => [ + 'inline_keyboard' => $inline_keyboard + ] ]; + } - // Encode to json - $reply_json = json_encode($reply_content); + // Create reply content array. + $reply_content = [ + 'method' => 'answerInlineQuery', + 'inline_query_id' => $query_id, + 'is_personal' => true, + 'cache_time' => 10, + 'results' => $results + ]; - // Send request to telegram api. - return curl_request($reply_json); + // Encode to json + $reply_json = json_encode($reply_content); + + // Send request to telegram api. + return curl_request($reply_json); } /** @@ -243,17 +252,15 @@ function answerInlineQuery($query_id, $contents) * @param $update * @param $message * @param $keys - * @param bool $merge_args + * @param array|bool $merge_args * @param $multicurl */ function edit_message($update, $message, $keys, $merge_args = false, $multicurl = false) { - if (isset($update['callback_query']['inline_message_id'])) { - $json_response = editMessageText($update['callback_query']['inline_message_id'], $message, $keys, NULL, $merge_args, $multicurl); - } else { - $json_response = editMessageText($update['callback_query']['message']['message_id'], $message, $keys, $update['callback_query']['message']['chat']['id'], $merge_args, $multicurl); - } - return $json_response; + if (isset($update['callback_query']['inline_message_id'])) { + return editMessageText($update['callback_query']['inline_message_id'], $message, $keys, NULL, $merge_args, $multicurl); + } + return editMessageText($update['callback_query']['message']['message_id'], $message, $keys, $update['callback_query']['message']['chat']['id'], $merge_args, $multicurl); } /** @@ -261,54 +268,54 @@ function edit_message($update, $message, $keys, $merge_args = false, $multicurl * @param $id_val * @param $text_val * @param $markup_val - * @param null $chat_id + * @param int|null $chat_id * @param mixed $merge_args * @param $multicurl */ function editMessageText($id_val, $text_val, $markup_val, $chat_id = NULL, $merge_args = false, $multicurl = false) { - // Create response array. - $response = [ - 'method' => 'editMessageText', - 'text' => $text_val, - 'parse_mode' => 'HTML', - 'reply_markup' => [ - 'inline_keyboard' => $markup_val - ] - ]; - - if ($markup_val == false) { - unset($response['reply_markup']); - $response['remove_keyboard'] = true; - } + // Create response array. + $response = [ + 'method' => 'editMessageText', + 'text' => $text_val, + 'parse_mode' => 'HTML', + 'reply_markup' => [ + 'inline_keyboard' => $markup_val + ] + ]; + + if ($markup_val == false) { + unset($response['reply_markup']); + $response['remove_keyboard'] = true; + } - // Valid chat id. - if ($chat_id != null) { - $response['chat_id'] = $chat_id; - $response['message_id'] = $id_val; - } else { - $response['inline_message_id'] = $id_val; - } + // Valid chat id. + if ($chat_id != null) { + $response['chat_id'] = $chat_id; + $response['message_id'] = $id_val; + } else { + $response['inline_message_id'] = $id_val; + } - // Write to log. - //debug_log($merge_args, 'K'); - //debug_log($response, 'K'); + // Write to log. + //debug_log($merge_args, 'K'); + //debug_log($response, 'K'); - if (is_array($merge_args) && count($merge_args)) { - $response = array_merge_recursive($response, $merge_args); - } + if (is_array($merge_args) && count($merge_args)) { + $response = array_merge_recursive($response, $merge_args); + } - if(!is_valid_target($chat_id, $id_val, true, false)){ - info_log("{$chat_id}/{$id_val}", 'ERROR: Cannot edit invalid chat/message id:'); - info_log($response, 'ERROR: data would have been:'); - exit(); - } - debug_log($response, '<-'); + if(!is_valid_target($chat_id, $id_val, true, false)){ + info_log("{$chat_id}/{$id_val}", 'ERROR: Cannot edit invalid chat/message id:'); + info_log($response, 'ERROR: data would have been:'); + exit(); + } + debug_log($response, '<-'); - // Encode response to json format. - $json_response = json_encode($response); - // Send request to telegram api. - return curl_request($json_response, $multicurl); + // Encode response to json format. + $json_response = json_encode($response); + // Send request to telegram api. + return curl_request($json_response, $multicurl); } /** @@ -321,12 +328,12 @@ function editMessageText($id_val, $text_val, $markup_val, $chat_id = NULL, $merg */ function edit_caption($update, $message, $keys, $merge_args = false, $multicurl = false) { - if (isset($update['callback_query']['inline_message_id'])) { - $json_response = editMessageCaption($update['callback_query']['inline_message_id'], $message, $keys, NULL, $merge_args, $multicurl); - } else { - $json_response = editMessageCaption($update['callback_query']['message']['message_id'], $message, $keys, $update['callback_query']['message']['chat']['id'], $merge_args, $multicurl); - } - return $json_response; + if (isset($update['callback_query']['inline_message_id'])) { + $json_response = editMessageCaption($update['callback_query']['inline_message_id'], $message, $keys, NULL, $merge_args, $multicurl); + } else { + $json_response = editMessageCaption($update['callback_query']['message']['message_id'], $message, $keys, $update['callback_query']['message']['chat']['id'], $merge_args, $multicurl); + } + return $json_response; } /** @@ -334,51 +341,51 @@ function edit_caption($update, $message, $keys, $merge_args = false, $multicurl * @param $id_val * @param $text_val * @param $markup_val - * @param null $chat_id + * @param int|null $chat_id * @param mixed $merge_args * @param $multicurl */ function editMessageCaption($id_val, $text_val, $markup_val, $chat_id = NULL, $merge_args = false, $multicurl = false) { - // Create response array. - $response = [ - 'method' => 'editMessageCaption', - 'caption' => $text_val, - 'parse_mode' => 'HTML', - 'reply_markup' => [ - 'inline_keyboard' => $markup_val - ] - ]; - - if ($markup_val == false) { - unset($response['reply_markup']); - $response['remove_keyboard'] = true; - } + // Create response array. + $response = [ + 'method' => 'editMessageCaption', + 'caption' => $text_val, + 'parse_mode' => 'HTML', + 'reply_markup' => [ + 'inline_keyboard' => $markup_val + ] + ]; + + if ($markup_val == false) { + unset($response['reply_markup']); + $response['remove_keyboard'] = true; + } - // Valid chat id. - if ($chat_id != null) { - $response['chat_id'] = $chat_id; - $response['message_id'] = $id_val; - } else { - $response['inline_message_id'] = $id_val; - } + // Valid chat id. + if ($chat_id != null) { + $response['chat_id'] = $chat_id; + $response['message_id'] = $id_val; + } else { + $response['inline_message_id'] = $id_val; + } - if (is_array($merge_args) && count($merge_args)) { - $response = array_merge_recursive($response, $merge_args); - } + if (is_array($merge_args) && count($merge_args)) { + $response = array_merge_recursive($response, $merge_args); + } - if(!is_valid_target($chat_id, $id_val, true, false)){ - info_log("{$chat_id}/{$id_val}", 'ERROR: Cannot edit invalid chat/message id:'); - info_log($response, 'ERROR: data would have been:'); - exit(); - } - debug_log($response, '<-'); + if(!is_valid_target($chat_id, $id_val, true, false)){ + info_log("{$chat_id}/{$id_val}", 'ERROR: Cannot edit invalid chat/message id:'); + info_log($response, 'ERROR: data would have been:'); + exit(); + } + debug_log($response, '<-'); - // Encode response to json format. - $json_response = json_encode($response); + // Encode response to json format. + $json_response = json_encode($response); - // Send request to telegram api. - return curl_request($json_response, $multicurl); + // Send request to telegram api. + return curl_request($json_response, $multicurl); } /** @@ -390,33 +397,33 @@ function editMessageCaption($id_val, $text_val, $markup_val, $chat_id = NULL, $m */ function editMessageReplyMarkup($id_val, $markup_val, $chat_id, $multicurl = false) { - // Create response array. - $response = [ - 'method' => 'editMessageReplyMarkup', - 'reply_markup' => [ - 'inline_keyboard' => $markup_val - ] - ]; - - // Valid chat id. - if ($chat_id != null) { - $response['chat_id'] = $chat_id; - $response['message_id'] = $id_val; - - } else { - $response['inline_message_id'] = $id_val; - } - if(!is_valid_target($chat_id, $id_val)){ - info_log("{$chat_id}/{$id_val}", 'ERROR: Cannot edit invalid chat/message id:'); - info_log($response, 'ERROR: data would have been:'); - exit(); - } - debug_log($response, '->'); + // Create response array. + $response = [ + 'method' => 'editMessageReplyMarkup', + 'reply_markup' => [ + 'inline_keyboard' => $markup_val + ] + ]; + + // Valid chat id. + if ($chat_id != null) { + $response['chat_id'] = $chat_id; + $response['message_id'] = $id_val; + + } else { + $response['inline_message_id'] = $id_val; + } + if(!is_valid_target($chat_id, $id_val)){ + info_log("{$chat_id}/{$id_val}", 'ERROR: Cannot edit invalid chat/message id:'); + info_log($response, 'ERROR: data would have been:'); + exit(); + } + debug_log($response, '->'); // Encode response to json format. - $json_response = json_encode($response); + $json_response = json_encode($response); - // Send request to telegram api. - return curl_request($json_response, $multicurl); + // Send request to telegram api. + return curl_request($json_response, $multicurl); } /** @@ -428,36 +435,36 @@ function editMessageReplyMarkup($id_val, $markup_val, $chat_id, $multicurl = fal */ function edit_message_keyboard($id_val, $markup_val, $chat_id, $multicurl = false) { - // Create response array. - $response = [ - 'method' => 'editMessageReplyMarkup', - 'reply_markup' => [ - 'inline_keyboard' => $markup_val - ] - ]; - - // Valid chat id. - if ($chat_id != null) { - $response['chat_id'] = $chat_id; - $response['message_id'] = $id_val; - - } else { - $response['inline_message_id'] = $id_val; - } - if(!is_valid_target($chat_id, $id_val)){ - info_log("{$chat_id}/{$id_val}", 'ERROR: Cannot edit invalid chat/message id:'); - info_log($response, 'ERROR: data would have been:'); - exit(); - } + // Create response array. + $response = [ + 'method' => 'editMessageReplyMarkup', + 'reply_markup' => [ + 'inline_keyboard' => $markup_val + ] + ]; + + // Valid chat id. + if ($chat_id != null) { + $response['chat_id'] = $chat_id; + $response['message_id'] = $id_val; + + } else { + $response['inline_message_id'] = $id_val; + } + if(!is_valid_target($chat_id, $id_val)){ + info_log("{$chat_id}/{$id_val}", 'ERROR: Cannot edit invalid chat/message id:'); + info_log($response, 'ERROR: data would have been:'); + exit(); + } - // Encode response to json format. - $json_response = json_encode($response); + // Encode response to json format. + $json_response = json_encode($response); - // Write to log. - debug_log($response, '->'); + // Write to log. + debug_log($response, '->'); - // Send request to telegram api. - return curl_request($json_response, $multicurl); + // Send request to telegram api. + return curl_request($json_response, $multicurl); } /** @@ -468,29 +475,29 @@ function edit_message_keyboard($id_val, $markup_val, $chat_id, $multicurl = fals */ function delete_message($chat_id, $message_id, $multicurl = false) { - // Create response content array. - $reply_content = [ - 'method' => 'deleteMessage', - 'chat_id' => $chat_id, - 'message_id' => $message_id, - ]; - if(!is_valid_target($chat_id, $message_id)){ - info_log("{$chat_id}/{$message_id}", 'ERROR: Cannot delete invalid chat/message id:'); - info_log($reply_content, 'ERROR: data would have been:'); - exit(); - } + // Create response content array. + $reply_content = [ + 'method' => 'deleteMessage', + 'chat_id' => $chat_id, + 'message_id' => $message_id, + ]; + if(!is_valid_target($chat_id, $message_id)){ + info_log("{$chat_id}/{$message_id}", 'ERROR: Cannot delete invalid chat/message id:'); + info_log($reply_content, 'ERROR: data would have been:'); + exit(); + } - // Encode data to json. - $reply_json = json_encode($reply_content); + // Encode data to json. + $reply_json = json_encode($reply_content); - // Set header to json. - header('Content-Type: application/json'); + // Set header to json. + header('Content-Type: application/json'); - // Write to log. - debug_log($reply_json, '>'); + // Write to log. + debug_log($reply_json, '>'); - // Send request to telegram api. - return curl_request($reply_json, $multicurl); + // Send request to telegram api. + return curl_request($reply_json, $multicurl); } /** @@ -500,28 +507,28 @@ function delete_message($chat_id, $message_id, $multicurl = false) */ function get_chat($chat_id, $multicurl = false) { - // Create response content array. - $reply_content = [ - 'method' => 'getChat', - 'chat_id' => $chat_id, - ]; - if(!is_valid_target($chat_id, null, false, true)){ - info_log($chat_id, 'ERROR: Cannot get invalid chat id:'); - info_log($reply_content, 'ERROR: data would have been:'); - exit(); - } + // Create response content array. + $reply_content = [ + 'method' => 'getChat', + 'chat_id' => $chat_id, + ]; + if(!is_valid_target($chat_id, null, false, true)){ + info_log($chat_id, 'ERROR: Cannot get invalid chat id:'); + info_log($reply_content, 'ERROR: data would have been:'); + exit(); + } - // Encode data to json. - $reply_json = json_encode($reply_content); + // Encode data to json. + $reply_json = json_encode($reply_content); - // Set header to json. - header('Content-Type: application/json'); + // Set header to json. + header('Content-Type: application/json'); - // Write to log. - debug_log($reply_json, '>'); + // Write to log. + debug_log($reply_json, '>'); - // Send request to telegram api. - return curl_request($reply_json, $multicurl); + // Send request to telegram api. + return curl_request($reply_json, $multicurl); } /** @@ -531,28 +538,28 @@ function get_chat($chat_id, $multicurl = false) */ function get_admins($chat_id, $multicurl = false) { - // Create response content array. - $reply_content = [ - 'method' => 'getChatAdministrators', - 'chat_id' => $chat_id, - ]; - if(!is_valid_target($chat_id, null, false, true)){ - info_log($chat_id, 'ERROR: Cannot get invalid chat id:'); - info_log($reply_content, 'ERROR: data would have been:'); - exit(); - } + // Create response content array. + $reply_content = [ + 'method' => 'getChatAdministrators', + 'chat_id' => $chat_id, + ]; + if(!is_valid_target($chat_id, null, false, true)){ + info_log($chat_id, 'ERROR: Cannot get invalid chat id:'); + info_log($reply_content, 'ERROR: data would have been:'); + exit(); + } - // Encode data to json. - $reply_json = json_encode($reply_content); + // Encode data to json. + $reply_json = json_encode($reply_content); - // Set header to json. - header('Content-Type: application/json'); + // Set header to json. + header('Content-Type: application/json'); - // Write to log. - debug_log($reply_json, '>'); + // Write to log. + debug_log($reply_json, '>'); - // Send request to telegram api. - return curl_request($reply_json, $multicurl); + // Send request to telegram api. + return curl_request($reply_json, $multicurl); } /** @@ -563,201 +570,207 @@ function get_admins($chat_id, $multicurl = false) */ function get_chatmember($chat_id, $user_id, $multicurl = false) { - // Create response content array. - $reply_content = [ - 'method' => 'getChatMember', - 'chat_id' => $chat_id, - 'user_id' => $user_id, - ]; - if(!is_valid_target($chat_id, null, false, true)){ - info_log($chat_id, 'ERROR: Cannot get invalid chat id:'); - info_log($reply_content, 'ERROR: data would have been:'); - exit(); - } + // Create response content array. + $reply_content = [ + 'method' => 'getChatMember', + 'chat_id' => $chat_id, + 'user_id' => $user_id, + ]; + if(!is_valid_target($chat_id, null, false, true)){ + info_log($chat_id, 'ERROR: Cannot get invalid chat id:'); + info_log($reply_content, 'ERROR: data would have been:'); + exit(); + } - // Encode data to json. - $reply_json = json_encode($reply_content); + // Encode data to json. + $reply_json = json_encode($reply_content); - // Set header to json. - header('Content-Type: application/json'); + // Set header to json. + header('Content-Type: application/json'); - // Write to log. - debug_log($reply_json, '>'); + // Write to log. + debug_log($reply_json, '>'); - // Send request to telegram api. - return curl_request($reply_json, $multicurl); + // Send request to telegram api. + return curl_request($reply_json, $multicurl); } /** * Send photo. - * @param $chat_id - * @param $photo_url - * @param array $text - * @param mixed $inline_keyboard + * @param $chatObj + * @param string $media_content content of the media file. + * @param bool $content_type true = photo file, false = file_id + * @param string|bool $text + * @param array|bool $inline_keyboard * @param array $merge_args - * @param array $multicurl - * @param int $identifier + * @param array|bool $multicurl + * @param int|bool $identifier */ -function send_photo($chat_id, $photo_url, $text = array(), $inline_keyboard = false, $merge_args = [], $multicurl = false, $identifier = false) +function send_photo($chatObj, $media_content, $content_type, $text = '', $inline_keyboard = false, $merge_args = [], $multicurl = false, $identifier = false) { - // Create response content array. - $reply_content = [ - 'method' => 'sendPhoto', - 'chat_id' => $chat_id, - 'photo' => $photo_url, - 'parse_mode' => 'HTML', - 'caption' => $text - ]; - if(!is_valid_target($chat_id, null, false, true)){ - info_log($chat_id, 'ERROR: Cannot send to invalid chat id:'); - info_log($reply_content, 'ERROR: data would have been:'); - exit(); - } + // Create response content array. + $post_contents = [ + 'method' => 'sendPhoto', + 'chat_id' => $chatObj['id'], + 'reply_markup' => json_encode(['inline_keyboard' => $inline_keyboard]), + ]; + if($text != '') { + $post_contents['caption'] = $text; + $post_contents['parse_mode'] = 'HTML'; + } + if(isset($chatObj['thread'])) $post_contents['message_thread_id'] = $chatObj['thread']; + if(!is_valid_target($chatObj['id'], null, false, true)){ + info_log($chatObj['id'], 'ERROR: Cannot send to invalid chat id:'); + info_log(print_r($post_contents, true), 'ERROR: data would have been:'); + exit(); + } - debug_log($inline_keyboard, 'KEYS:'); + $post_contents['photo'] = ($content_type) ? new CURLStringFile($media_content, 'photo') : $media_content; - if (isset($inline_keyboard)) { - $reply_content['reply_markup'] = ['inline_keyboard' => $inline_keyboard]; - } - - if (is_array($merge_args) && count($merge_args)) { - $reply_content = array_merge_recursive($reply_content, $merge_args); - } + debug_log($inline_keyboard, 'KEYS:'); - // Encode data to json. - $reply_json = json_encode($reply_content); - header('Content-Type: application/json'); + if (is_array($merge_args) && count($merge_args)) { + $post_contents = array_merge_recursive($post_contents, $merge_args); + } - debug_log($reply_json, '>'); + // Don't log the binary portion + $log_contents = array_merge(array(), $post_contents); + $log_contents['photo'] = '[binary data]'; + debug_log(print_r($log_contents, true), '>'); - // Send request to telegram api. - return curl_request($reply_json, $multicurl, $identifier); + // Send request to telegram api. + return curl_request($post_contents, $multicurl, $identifier); } /** * Edit message media and text. * @param $id_val * @param $text_val - * @param $url + * @param string $media_content content of the media file. + * @param bool $content_type true = photo file, false = file_id/url * @param $markup_val - * @param null $chat_id + * @param int|null $chat_id * @param mixed $merge_args * @param $multicurl */ -function editMessageMedia($id_val, $text_val, $url, $markup_val, $chat_id = NULL, $merge_args = false, $multicurl = false) +function editMessageMedia($id_val, $text_val, $media_content, $content_type, $inline_keyboard = false, $chat_id = NULL, $merge_args = false, $multicurl = false, $identifier = false) { - // Create response array. - $response = [ - 'method' => 'editMessageMedia', - 'media' => [ - 'type' => 'photo', - 'media' => $url, - 'caption' => $text_val, - 'parse_mode'=> 'HTML' - ], - 'reply_markup' => [ - 'inline_keyboard' => $markup_val - ] - ]; - - if ($markup_val == false) { - unset($response['reply_markup']); - $response['remove_keyboard'] = true; - } - if ($chat_id != null) { - $response['chat_id'] = $chat_id; - $response['message_id'] = $id_val; - } else { - $response['inline_message_id'] = $id_val; - } - if (is_array($merge_args) && count($merge_args)) { - $response = array_merge_recursive($response, $merge_args); - } - if(!is_valid_target($chat_id, $id_val, true, false)){ - info_log("{$chat_id}/{$id_val}", 'ERROR: Cannot edit media of invalid chat/message id:'); - info_log($response, 'ERROR: data would have been:'); - exit(); - } + // Create response array. + $post_contents = [ + 'method' => 'editMessageMedia', + 'media' => [ + 'type' => 'photo', + 'caption' => $text_val, + 'parse_mode'=> 'HTML' + ] + ]; + + if ($inline_keyboard !== false) { + $post_contents['reply_markup'] = json_encode(['inline_keyboard' => $inline_keyboard]); + }else { + $post_contents['remove_keyboard'] = true; + } + if ($chat_id != null) { + $post_contents['chat_id'] = $chat_id; + $post_contents['message_id'] = $id_val; + } else { + $post_contents['inline_message_id'] = $id_val; + } + if (is_array($merge_args) && count($merge_args)) { + $post_contents = array_merge_recursive($post_contents, $merge_args); + } + if(!is_valid_target($chat_id, $id_val, true, false)){ + info_log("{$chat_id}/{$id_val}", 'ERROR: Cannot edit media of invalid chat/message id:'); + info_log(print_r($post_contents, true), 'ERROR: data would have been:'); + exit(); + } - // Encode response to json format. - $json_response = json_encode($response); - debug_log($response, '<-'); + // Encode response to json format. + if($content_type) { + $post_contents['photo'] = new CURLStringFile($media_content, 'photo'); + $post_contents['media']['media'] = 'attach://photo'; + } else { + $post_contents['media']['media'] = $media_content; + } + $post_contents['media'] = json_encode($post_contents['media']); + debug_log("Editing message ".$post_contents['message_id']." media: ".$post_contents['media'], '->'); - // Send request to telegram api. - return curl_request($json_response, $multicurl); + // Send request to telegram api. + return curl_request($post_contents, $multicurl, $identifier); } /** * Send request to telegram api - single or multi?. - * @param $json + * @param $post_contents * @param $multicurl * @param $identifier * @return mixed */ -function curl_request($json, $multicurl = false, $identifier = false) +function curl_request($post_contents, $multicurl = false, $identifier = false) { - // Send request to telegram api. - if($multicurl == true) { - return ['json' => $json, 'identifier' => $identifier]; - } else { - return curl_json_request($json, $identifier); - } + // Send request to telegram api. + if($multicurl == true) { + return ['post_contents' => $post_contents, 'identifier' => $identifier]; + } else { + return curl_json_request($post_contents, $identifier); + } } /** * Send request to telegram api. - * @param $json + * @param $post_contents * @param $identifier * @return mixed */ -function curl_json_request($json, $identifier) +function curl_json_request($post_contents, $identifier) { - global $config; - // Bridge mode? - if($config->BRIDGE_MODE) { - // Add bot folder name to callback data - debug_log('Adding bot folder name "' . basename(ROOT_PATH) . '" to callback data'); - $search = '"callback_data":"'; - $replace = $search . basename(ROOT_PATH) . ':'; - $json = str_replace($search,$replace,$json); - } + global $config; + + // Telegram + $URL = 'https://api.telegram.org/bot' . API_KEY . '/'; + $curl = curl_init($URL); + + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + if(!is_array($post_contents)) curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-type: application/json")); + curl_setopt($curl, CURLOPT_POST, true); + curl_setopt($curl, CURLOPT_POSTFIELDS, $post_contents); + curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($curl, CURLOPT_TIMEOUT, 10); + + // Use Proxyserver for curl if configured + if ($config->CURL_USEPROXY && !empty($config->CURL_PROXYSERVER)) { + curl_setopt($curl, CURLOPT_PROXY, $config->CURL_PROXYSERVER); + } + // Use only ipv4 if configured + if($config->CURL_IPRESOLVE_V4) { + curl_setopt($curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + } - // Telegram - $URL = 'https://api.telegram.org/bot' . API_KEY . '/'; - $curl = curl_init($URL); - - curl_setopt($curl, CURLOPT_HEADER, false); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-type: application/json")); - curl_setopt($curl, CURLOPT_POST, true); - curl_setopt($curl, CURLOPT_POSTFIELDS, $json); - curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5); - curl_setopt($curl, CURLOPT_TIMEOUT, 10); - - // Use Proxyserver for curl if configured - if ($config->CURL_USEPROXY && !empty($config->CURL_PROXYSERVER)) { - curl_setopt($curl, CURLOPT_PROXY, $config->CURL_PROXYSERVER); - } + // Write to log. + if(is_array($post_contents)) debug_log(print_r($post_contents,true), '->'); + else debug_log($post_contents, '->'); - // Write to log. - debug_log($json, '->'); + // Execute curl request. + $json_response = curl_exec($curl); - // Execute curl request. - $json_response = curl_exec($curl); + if($json_response === false) { + info_log(curl_error($curl)); + curl_close($curl); + return false; + } - if($json_response === false) { - info_log(curl_error($curl)); - } + // Close connection. + curl_close($curl); - // Close connection. - curl_close($curl); + // Process response from telegram api. + responseHandler($json_response, $post_contents); - // Process response from telegram api. - $response = curl_json_response($json_response, $json, $identifier); + $responseArray = json_decode($json_response, true); + collectCleanup($responseArray, $post_contents, $identifier); - // Return response. - return $response; + // Return response. + return $responseArray; } /** @@ -767,81 +780,191 @@ function curl_json_request($json, $identifier) */ function curl_json_multi_request($json) { - global $config; - // Set URL. - $URL = 'https://api.telegram.org/bot' . API_KEY . '/'; - - // Curl handles. - $curly = array(); - - // Curl response. - $response = array(); - - // Init multi handle. - $mh = curl_multi_init(); - - // Loop through json array, create curl handles and add them to the multi-handle. - foreach ($json as $id => $data) { - // Init. - $curly[$id] = curl_init(); - - // Curl options. - curl_setopt($curly[$id], CURLOPT_URL, $URL); - curl_setopt($curly[$id], CURLOPT_HEADER, false); - curl_setopt($curly[$id], CURLOPT_HTTPHEADER, array("Content-type: application/json")); - curl_setopt($curly[$id], CURLOPT_RETURNTRANSFER, true); - curl_setopt($curly[$id], CURLOPT_CONNECTTIMEOUT, 5); - curl_setopt($curly[$id], CURLOPT_TIMEOUT, 10); - - // Use Proxyserver for curl if configured. - if($config->CURL_USEPROXY && !empty($config->CURL_PROXYSERVER)) { - curl_setopt($curly[$id], CURLOPT_PROXY, $config->CURL_PROXYSERVER); - } - - // Bridge mode? - if($config->BRIDGE_MODE) { - // Add bot folder name to callback data - debug_log('Adding bot folder name "' . basename(ROOT_PATH) . '" to callback data'); - $search = '"callback_data":"'; - $replace = $search . basename(ROOT_PATH) . ':'; - array_push($data['json'], str_replace($search,$replace,$data['json'])); - } - - // Curl post. - curl_setopt($curly[$id], CURLOPT_POST, true); - curl_setopt($curly[$id], CURLOPT_POSTFIELDS, $data['json']); - - // Add multi handle. - curl_multi_add_handle($mh, $curly[$id]); - - // Write to log. - debug_log($data['json'], '->'); + global $config; + // Set URL. + $URL = 'https://api.telegram.org/bot' . API_KEY . '/'; + + // Curl handles. + $curly = array(); + + // Curl response. + $response = array(); + + // Init multi handle. + $mh = curl_multi_init(); + + // Loop through json array, create curl handles and add them to the multi-handle. + foreach ($json as $id => $data) { + // Init. + $curly[$id] = curl_init(); + + // Curl options. + curl_setopt($curly[$id], CURLOPT_URL, $URL); + curl_setopt($curly[$id], CURLOPT_HEADER, false); + if(!is_array($data['post_contents'])) curl_setopt($curly[$id], CURLOPT_HTTPHEADER, array("Content-type: application/json")); + curl_setopt($curly[$id], CURLOPT_RETURNTRANSFER, true); + curl_setopt($curly[$id], CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($curly[$id], CURLOPT_TIMEOUT, 10); + + // Use Proxyserver for curl if configured. + if($config->CURL_USEPROXY && !empty($config->CURL_PROXYSERVER)) { + curl_setopt($curly[$id], CURLOPT_PROXY, $config->CURL_PROXYSERVER); + } + // Use only ipv4 if configured + if($config->CURL_IPRESOLVE_V4) { + curl_setopt($curly[$id], CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + } + + // Curl post. + curl_setopt($curly[$id], CURLOPT_POST, true); + curl_setopt($curly[$id], CURLOPT_POSTFIELDS, $data['post_contents']); + + // Add multi handle. + curl_multi_add_handle($mh, $curly[$id]); + + // Don't log the binary data of a photo + $content = $data['post_contents']; + $log_content = $content; + if(is_array($content)) { + if(isset($content['photo']) && is_object($content['photo']) or isBinary($content['photo']) ) { + $log_content = array_merge([],$content); + $log_content['photo'] = '[binary content]'; + } + } else { + if(isBinary($content)) { + $log_content = '[binary content]'; + } + } + if(is_array($log_content)) debug_log(print_r($log_content, true), '->'); + else debug_log($log_content, '->'); + } + + // Get content and remove handles. + $retry = false; + $maxRetries = 3; + $retryCount = 0; + $sleep = 0; + do { + // On the second pass and onwards sleep before executing curls + if($retry === true) { + $retry = false; + $sleep = 0; + $retryCount++; + debug_log('Retrying in '.$sleep.' seconds'); + sleep($sleep); + debug_log('Retry count: '.($retryCount).'...'); } // Execute the handles. $running = null; do { - curl_multi_select($mh); - $status = curl_multi_exec($mh, $running); + $status = curl_multi_exec($mh, $running); + curl_multi_select($mh); } while($running > 0 && $status === CURLM_OK); if ($status != CURLM_OK) { - info_log(curl_multi_strerror($status)); + info_log(curl_multi_strerror($status)); } - // Get content and remove handles. foreach($curly as $id => $content) { - $response[$id] = curl_multi_getcontent($content); - curl_multi_remove_handle($mh, $content); - } + $response[$id] = curl_multi_getcontent($content); + curl_multi_remove_handle($mh, $content); + $responseResults = responseHandler($response[$id], $json[$id]['post_contents']); + // Handle errors + if(is_array($responseResults) && $responseResults[0] === 'retry') { + $retry = true; + // Use the highest sleep value returned by TG + $sleep = $responseResults[1] > $sleep ? $responseResults[1] : $sleep; + // Re-add this handle with the same info + curl_multi_add_handle($mh, $curly[$id]); + continue; + } + + unset($curly[$id]); + } + }while($retry === true && $retryCount < $maxRetries); + + $responseArrays = []; + // Process response from telegram api. + foreach($response as $id => $json_response) { + $responseArrays[$id] = json_decode($response[$id], true); + collectCleanup($responseArrays[$id], $json[$id]['post_contents'], $json[$id]['identifier']); + } - // Process response from telegram api. - foreach($response as $id => $json_response) { - $response[$id] = curl_json_response($response[$id], $json[$id]['json'], $json[$id]['identifier']); - debug_log_incoming($json_response, '<-'); - } + // Return response. + return $responseArrays; +} +if($metrics) { + $tg_response_code = $metrics->registerCounter($namespace, 'tg_response_count', 'Counters of response codes from Telegram', ['code', 'method', 'description']); +} - // Return response. - return $response; +/** + * Determine whether the given value is a binary string by checking to see if it has detectable character encoding. + * Non-strings are treated as binary. + * + * @param string $value + * + * @return bool + */ +function isBinary($value): bool +{ + if(is_string($value)){ + return false === mb_detect_encoding((string)$value, null, true); + } + return true; } +/** + * Process response from Telegram. + * @param $jsonResponse string JSON string returned by Telegram + * @param $request string|array The request we sent to Telegram + * @return mixed + */ +function responseHandler($jsonResponse, $request) { + global $metrics, $tg_response_code; + // Write to log. + debug_log_incoming($jsonResponse, '<-'); + + // Decode json objects + $request_array = is_array($request) ? $request : json_decode($request, true); + $response = json_decode($jsonResponse, true); + if ($metrics){ + $code = 200; + $method = $request_array['method']; + $description = null; + if (isset($response['error_code'])) { + $code = $response['error_code']; + # We have to also include the description because TG overloads error codes + $description = $response['description']; + } + $tg_response_code->inc([$code, $method, $description]); + } + // Validate response. + if ((isset($response['ok']) && $response['ok'] != true) || isset($response['update_id'])) { + if(is_array($request)) $json = json_encode($request); else $json = $request; + // Handle some specific errors + if($response['description'] == 'Bad Request: message to edit not found' || $response['description'] == 'Bad Request: message to delete not found') { + // Loop through tables where we store sent messages + $table = ['cleanup', 'overview', 'trainerinfo']; + $i = 0; + do { + $q = my_query('DELETE FROM '.$table[$i].' WHERE chat_id = :chatId AND message_id = :messageId',['chatId' => $request_array['chat_id'], ':messageId' => $request_array['message_id']]); + $i++; + } while($q->rowCount() == 0 && $i < count($table)); + info_log($table[$i-1], 'A message was deleted by someone else than us. Deleting info from database table:'); + info_log($request_array['chat_id'], 'chat_id:'); + info_log($request_array['message_id'], 'message_id:'); + return true; + } + if(substr($response['description'], 0, 30) == 'Too Many Requests: retry after') { + return [ + 'retry', + ($response['parameters']['retry_after'] + 1), + ]; + } + info_log("{$json} -> {$jsonResponse}", 'ERROR:'); + // Log unhandled errors + return false; + } + return true; +} diff --git a/core/tools/automate_raid_hour_creation/config.json.example b/core/tools/automate_raid_hour_creation/config.json.example index 61312b10..8c8d7da3 100644 --- a/core/tools/automate_raid_hour_creation/config.json.example +++ b/core/tools/automate_raid_hour_creation/config.json.example @@ -6,10 +6,12 @@ "RAID_HOUR_EVENT_ID":"2", "RAID_CREATOR_ID":"", "CHAT_ID": "", + "TIMEZONE": "Europe/Helsinki", "GYM_INFOS": [ - [1,"<-Gym id there and event note for 1st gym here"], - [2,"<-Gym id there and event note for 2nd gym here"], - [3,"<-Gym id there and event note for 3rd gym here"] - ], - "URL": "https://www.yourhost.com/PokemonRaidBot/index.php?apikey=123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11" + [1,"<-Gym id there and event note for 1st gym here"], + [2,"<-Gym id there and event note for 2nd gym here"], + [3,"<-Gym id there and event note for 3rd gym here"] + ], + "URL": "https://www.yourhost.com/PokemonRaidBot/index.php?apikey=123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11", + "EXTRA_DATES":[] } diff --git a/core/tools/automate_raid_hour_creation/raid_hour_creator.php b/core/tools/automate_raid_hour_creation/raid_hour_creator.php index c7aa6695..95bfcb78 100644 --- a/core/tools/automate_raid_hour_creation/raid_hour_creator.php +++ b/core/tools/automate_raid_hour_creation/raid_hour_creator.php @@ -11,13 +11,16 @@ die('Config file not valid JSON, cannot continue.'); } +$tz = $config->TIMEZONE; +date_default_timezone_set($tz); + // Establish mysql connection. // TODO(artanicus): This should be centralized & imported instead of duplicated $dbh = new PDO('mysql:host=' . $config->DB_HOST . ';dbname=' . $config->DB_NAME . ';charset=utf8mb4', $config->DB_USER, $config->DB_PASSWORD, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)); $dbh->setAttribute(PDO::ATTR_ORACLE_NULLS, PDO::NULL_EMPTY_STRING); $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); -$link = 'https://raw.githubusercontent.com/ccev/pogoinfo/v2/active/events.json'; +$link = 'https://raw.githubusercontent.com/bigfoott/ScrapedDuck/refs/heads/data/events.min.json'; $data = curl_get_contents($link); $raids = $dbh->prepare("SELECT start_time FROM raids WHERE event = '" . $config->RAID_HOUR_EVENT_ID . "'"); $raids->execute(); @@ -25,6 +28,7 @@ $offset = date('Z'); $interval = DateInterval::createFromDateString($offset.' seconds'); $raid_to_create = []; +$datesToCreate = []; $data = json_decode($data,true); if($data !== false) { $now = new DateTime('18:00'); @@ -37,8 +41,15 @@ $end->sub($interval); $event_start = $start->format('Y-m-d H:i:s'); $event_end = $end->format('Y-m-d H:i:s'); - if($event['type'] == 'raid-hour' && (($raids->rowcount() > 0 && !in_array($event_start, $raids_res)) || $raids->rowcount() === 0)) { - $mon_name = trim(str_replace('Raid Hour','',str_replace('Forme','',$event['name']))); + $notePrefix = null; + if($event['eventType'] == 'raid-hour' && $start > $now && (($raids->rowcount() > 0 && !in_array($event_start, $raids_res)) || $raids->rowcount() === 0)) { + if(preg_match('/&/', $event['name'])) { + $notePrefix = $event['name'] . PHP_EOL; + $raid_to_create[] = [9995, 0, $event_start, $event_end, '5', $notePrefix]; + $datesToCreate[] = $event_start; + continue; + } + $mon_name = trim(preg_replace('/Forme|Form|Raid Hour|[()]/','',$event['name'])); $mon_split = explode(' ',$mon_name); $part_count = count($mon_split); $mon_query_input = []; @@ -66,61 +77,79 @@ if($mon === false) continue; $pokemon = $mon[0]; $pokemon_form = $mon[1]; + $notePrefix = $event['name'] . PHP_EOL; } - $raid_to_create[] = [$pokemon, $pokemon_form,$event_start,$event_end]; + $raid_to_create[] = [$pokemon, $pokemon_form,$event_start,$event_end, '5', $notePrefix]; + $datesToCreate[] = $event_start; } } - if($now->format('w') == 3 && !in_array($now->format('Y-m-d H:i:s'),$raids_res)) { + if(($now->format('w') == 3 || in_array($now->format('Y-m-d'), $config->EXTRA_DATES)) && !in_array($now->format('Y-m-d H:i:s'), $raids_res) && !in_array($now->format('Y-m-d H:i:s'), $datesToCreate)) { $start_time = gmdate('Y-m-d H:i:s',mktime(18,0,0)); $end_time = gmdate('Y-m-d H:i:s',mktime(19,0,0)); $mon = get_current_bosses($start_time); - if($mon !== false) $raid_to_create[] = [$mon[0], $mon[1] ,$start_time, $end_time]; + if($mon !== false) $raid_to_create[] = [$mon[0], $mon[1] ,$start_time, $end_time, $mon[2], null]; } } try { - $query = $dbh->prepare('INSERT INTO raids ( - user_id, - pokemon, - pokemon_form, - spawn, - level, - start_time, - end_time, - gym_id, - event, - event_note - ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - '); + $query = $dbh->prepare(' + INSERT INTO raids ( + user_id, + pokemon, + pokemon_form, + spawn, + level, + start_time, + end_time, + gym_id, + event, + event_note + ) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + '); + $now = gmdate('Y-m-d H:i:s'); foreach($raid_to_create as $raid_info) { foreach($config->GYM_INFOS as $t) { - $now = gmdate('Y-m-d H:i:s'); - $query->execute([$config->RAID_CREATOR_ID,$raid_info[0],$raid_info[1],$now,'5',$raid_info[2],$raid_info[3],$t[0],$config->RAID_HOUR_EVENT_ID,$t[1]]); + $query->execute([ + $config->RAID_CREATOR_ID, + $raid_info[0], + $raid_info[1], + $now, + $raid_info[4], + $raid_info[2], + $raid_info[3], + $t[0], + $config->RAID_HOUR_EVENT_ID, + $raid_info[5] . $t[1] + ]); } } } catch (PDOException $exception) { echo($exception->getMessage()); - $dbh = null; exit; } -$dbh = null; function get_current_bosses($spawn) { global $dbh; - $pk = $dbh->prepare('SELECT pokedex_id,pokemon_form_id FROM raid_bosses WHERE raid_level = \'5\' AND \''.$spawn.'\' BETWEEN date_start AND date_end'); - $pk->execute(); - $res = $pk->fetch(); - if($pk->rowCount()==1) { - $pokemon = $res['pokedex_id']; - $pokemon_form = $res['pokemon_form_id']; - }elseif($pk->rowCount()>1) { - $pokemon = 9995; - $pokemon_form = 0; - }else { - return false; + $i = 0; + $levels = [5, 7, 8]; // Search potential raid hour bosses from these raid levels + $pokemon = $pokemon_form = false; + foreach($levels as $level) { + $pk = $dbh->prepare('SELECT pokedex_id,pokemon_form_id FROM raid_bosses WHERE raid_level = ? AND ? BETWEEN date_start AND date_end and disabled = 0'); + $pk->execute([$level, $spawn]); + $res = $pk->fetch(); + if($pk->rowCount() == 1) { + $pokemon = $res['pokedex_id']; + $pokemon_form = $res['pokemon_form_id']; + break; + }elseif($pk->rowCount() > 1) { + $pokemon = 999 . $level; + $pokemon_form = 0; + break; + } } - return [$pokemon,$pokemon_form]; + if($pokemon === false) return false; + return [$pokemon, $pokemon_form, $level]; } function curl_get_contents($url) { @@ -139,5 +168,3 @@ function curl_get_contents($url) curl_close($ch); return $content; } - -?> diff --git a/core/tools/automate_raid_hour_creation/share_event_raids.php b/core/tools/automate_raid_hour_creation/share_event_raids.php index 13bd629b..255110bc 100644 --- a/core/tools/automate_raid_hour_creation/share_event_raids.php +++ b/core/tools/automate_raid_hour_creation/share_event_raids.php @@ -49,6 +49,3 @@ // Close connection. curl_close($curl); } - -$dbh = null; -?> diff --git a/core/tools/hash.php b/core/tools/hash.php index 8fea50c1..bf087ec1 100644 --- a/core/tools/hash.php +++ b/core/tools/hash.php @@ -1,4 +1,3 @@ diff --git a/core/tools/set_commands.php b/core/tools/set_commands.php index 2054f1c6..7cdc1bcd 100644 --- a/core/tools/set_commands.php +++ b/core/tools/set_commands.php @@ -48,7 +48,7 @@ $request = json_encode([ 'method'=>'deleteMyCommands', 'scope'=>$scope, - + ]); $response = curl_json_request($request); } diff --git a/core/tools/webhook.html b/core/tools/webhook.html index 92bf78f3..2e03f318 100644 --- a/core/tools/webhook.html +++ b/core/tools/webhook.html @@ -15,6 +15,11 @@


+ +

+ +

+

@@ -64,6 +69,7 @@ el: '#app', data: { token: '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11', + botname: '', maxconnection: '80', port: '', host: 'https://' + window.location.hostname + location.pathname.replace(/[^/]*$/, '') @@ -94,6 +100,9 @@ var bot = document.createElement('a'); bot.href = this.host; bot_string = bot.origin + port_string + bot.pathname.replace(/\/?$/, '/') + 'index.php?apikey=' + this.token; + if (this.botname) { + bot_string = bot_string + '&bot_name=' + this.botname + } return bot_string } diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index d37257ee..727b33fb 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -1,7 +1,6 @@ -version: "3.7" - services: raidbot: + container_name: raidbot ports: - 8088:80 depends_on: @@ -9,8 +8,10 @@ services: restart: always volumes: # These are just example paths, change them to where you have stored the data! - - ./PokemonRaidBot/config/:/var/www/html/config/ - ./PokemonRaidBot/access/:/var/www/html/access/ + - ./PokemonRaidBot/config/config.json:/var/www/html/config/config.json + - ./PokemonRaidBot/custom/:/var/www/html/custom/ + - ./PokemonRaidBot/images/gyms/:/var/www/html/images/gyms - ./PokemonRaidBot/images/pokemon_PokeMiners/:/var/www/html/images/pokemon_PokeMiners - ./tg-logs/:/var/log/tg-bots/ environment: @@ -26,7 +27,8 @@ services: ofelia.job-exec.raidbot-overview.command: /usr/bin/curl -s -k -d '{"callback_query":{"data":"0:overview_refresh:0"}}' https://raidbot.example.com/index.php?apikey=changeme raidbot-db: - image: mariadb:10.3 + container_name: raidbot-db + image: mariadb:10.11 restart: always command: ['mysqld', '--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci'] volumes: @@ -41,6 +43,7 @@ services: MYSQL_PASSWORD: ofelia: + container_name: raidbot-ofelia image: mcuadros/ofelia:latest depends_on: - raidbot diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index acb25a8c..3eb00bd1 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -34,7 +34,7 @@ env | egrep -q '^POKEMONRAIDBOT_' && \ | tee -a config/config.json) # Do some permission setting for any data that might've been volume mounted in, otherwise stuff will fail in weird ways. -chown -R www-data:www-data config/config.json access/ custom/ images/pokemon* +chown -R www-data:www-data config/config.json access/ custom/ images/gyms/ images/pokemon* # Ensure we now have a valid config if [[ ! -a /var/www/html/config/config.json ]]; then diff --git a/docs/config.rst b/docs/config.rst index 49476b55..e2637f18 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -47,7 +47,6 @@ it's already prepended to the right length. .. code-block:: - { *snip* "chat": { "id": -1002233445566, @@ -95,6 +94,22 @@ How to make a Supergroup * Once a group has been converted to a Supergroup it cannot go back to a normal Group, even if you change back the option that caused it to convert. * Be aware that the group ID will change completely when the group gets converted so you'll need to find it again! +Configuring multiple bots +------------------------- + +If you want to run multiple Telegram bots using this directory, you can create for each of them config and access files by prepending them with a unique name: + +.. code-block:: + + access/bot1-creator111222333 + access/bot2-creator111222333 + config/bot1-config.json + config/bot1-geoconfig.json + config/bot2-config.json + config/bot2-geoconfig.json + +When setting webhook to Telegram using ``webhook.html`` you must set the matching bot name. + Database connection ------------------- @@ -111,8 +126,6 @@ Set ``APIKEY_HASH`` to the hashed value of your bot token (preferably lowercase) Set ``DDOS_MAXIMUM`` to the amount of callback queries each user is allowed to do each minute. If the amount is reached any further callback query is rejected by the DDOS check. Default value: 10. -Set ``BRIDGE_MODE`` to true when you're using the PokemonBotBridge. If you're not using the PokemonBotBridge the default value of false is used. PokemonBotBridge: https://github.com/pokepark/PokemonBotBridge - More config options ------------------- @@ -138,6 +151,7 @@ You can set several languages for the bot. Available languages are (A-Z): * DE (German) * EN (English) +* ES (Spanish) * FI (Finnish) * FR (French) * IT (Italian) @@ -194,6 +208,8 @@ OpenStreetMap API To use OpenStreetMap's Nominatim API to lookup addresses of gyms, set ``OSM_LOOKUP`` to ``true`` and ``MAPS_LOOKUP`` to ``false``. +You can set a custom nominatim server address in ``OSM_URL``, e.g. ``http://localhost:8090``. + Quote from `Nominatim documentation `_\ : ``The reverse geocoding API does not exactly compute the address for the coordinate it receives. It works by finding the closest suitable OSM object and returning its address information. This may occasionally lead to unexpected results.`` @@ -211,18 +227,6 @@ Set ``RAID_EGG_DURATION`` to the maximum amount of minutes a user can select for Set ``RAID_DURATION`` to the maximum amount of minutes a user can select as raid duration for already running/active raids. -Set ``RAID_HOUR`` to true to enable the raid hour. Enabling the raid hour superseds the normal raid duration. Note that the raid hour takes precedence over the raid day. Make sure to disable the raid hour to get the raid day. - -Set ``RAID_HOUR_DURATION`` to the maximum amount of minutes a user can select as raid duration if the ``RAID_HOUR`` is enabled. Per default max. 60 minutes. - -Set ``RAID_HOUR_CREATION_LIMIT`` to the maximum amount of raids a user can create if the ``RAID_HOUR`` is enabled. Per default 1 raid. - -Set ``RAID_DAY`` to true to enable the raid day. Enabling the raid day superseds the normal raid duration. Note that the raid hour takes precedence over the raid day. Make sure to disable the raid hour to get the raid day. - -Set ``RAID_DAY_DURATION`` to the maximum amount of minutes a user can select as raid duration if the ``RAID_DAY`` is enabled. Per default max. 180 minutes. - -Set ``RAID_DAY_CREATION_LIMIT`` to the maximum amount of raids a user can create if the ``RAID_DAY`` is enabled. Per default 1 raid. - Set ``RAID_DURATION_CLOCK_STYLE`` to customize the default style for the raid start time selection. Set to true, the bot will show the time in clocktime style, e.g. "18:34" as selection when the raid will start. Set to false the bot will show the time until the raid starts in minutes, e.g. "0:16" (similar to the countdown in the gyms). Users can switch between both style in the raid creation process. Set ``RAID_CUSTOM_GYM_LETTERS`` to further split gyms by their first letter. For example if you have a lot of gyms starting with 'St' as there are a lot of churches like St. Helen, St. Jospeh, etc. in your area and the gym list under the letter 'S' is too long, you can tell the bot to put the gyms starting with 'St' under 'St' and exclude them from the letter 'S'. There is no limitation in length, so even 'Berlin' would work to split gyms, but the recommendation is to use as less chars as possible to split the gyms. You can add multiple custom gym letters, just separate them by comma. Example: ``"RAID_CUSTOM_GYM_LETTERS":"Ber,Sch,St,Wi"`` @@ -273,6 +277,13 @@ Set ``RAID_EX_GYM_MARKER`` to set the marker for ex-raid gyms. You can use a pre Set ``RAID_CREATION_EX_GYM_MARKER`` to true to show the marker for ex-raid gyms during raid creation. +Manage bot configuration values via Telegram +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +For bot admins to easily manage specific bot settings you can create a config file ``config/telegram.json`` containing the configuration values you want to be able to edit. Example file is located in ``config/defaults-telegram.json``. + +Users with the right permissions can then use the commands ``/get`` and ``/set`` to manage those configuration values. + Automatically refreshing raid polls ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -295,10 +306,12 @@ For a specific chat: Raid Picture mode ^^^^^^^^^^^^^^^^^ -To enable raid announcements as images set ``RAID_PICTURE`` to true and set the url in ``RAID_PICTURE_URL`` to the location of raidpicture.php. +To enable raid announcements as images set ``RAID_PICTURE`` to true. You also need to get the Pokemon sprites from known sources and put them in either images/pokemon/ or the images/pokemon_REPO-OWNER/ folder. The images/pokemon/ directory needs to be created manually, the images/pokemon_REPO-OWNER/ folders will be created automatically when by running the special download script mentioned below. +If you have an UICONS repo stored on your server already you can softlink the ``pokemon`` folder from there to ``images/pokemon/`` in raidbot directory. + Pokemon Icons / Sprites: Link: https://github.com/PokeMiners/pogo_assets/tree/master/Images/Pokemon%20-%20256x256 @@ -365,7 +378,7 @@ Sharing raid polls can be restricted, so only specific chats/users can be allowe With a predefined list ``SHARE_CHATS`` you can specify the chats which should appear as buttons for sharing raid polls. -You can define different chats for specific raid levels using ``SHARE_CHATS_LEVEL_`` plus the raid level too. Raid levels can be 'X', '5', '4', '3', '2' or '1'. +You can define different chats for specific raid levels using ``SHARE_CHATS_LEVEL_`` plus the raid level too. For the ID of a chat either forward a message from the chat to a bot like @RawDataBot, @getidsbot or search the web for another method ;) @@ -389,12 +402,31 @@ Predefine sharing all raids to the chats -100111222333 and -100444555666, except Raids from Webhook ~~~~~~~~~~~~~~~~~~ -You can receive Raids from a mapping system such as MAD via Webhook. -For that you need to setup ``WEBHOOK_CREATOR``\ , and to automatically share raids to chats, +You can receive Raids from a mapping systems such as MAD and RDM via Webhook. +For that you need to setup ``WEBHOOK_CREATOR``\ , and to automatically share raids to chats, ``"WEBHOOK_CHATS_ALL_LEVELS":"-100444555666"`` or by Raidlevel ``"WEBHOOK_CHATS_LEVEL_5":"-100444555666"`` All incoming raids will be published in these chats. +If you only want to automatically share a specific Pokemon, you can do that by editing the ``WEBHOOK_CHATS_BY_POKEMON`` json array: + + +.. code-block:: + + "WEBHOOK_CHATS_BY_POKEMON" : [ + { + "pokemon_id": 744, + "chats":[chat_id_1, chat_id_2] + }, + { + "pokemon_id": 25, + "form_id": 2678, + "chats":[chat_id_3] + } + ], + +``pokemon_id`` and ``chats`` are required objects, ``form_id`` is optional. + Filter Raids from Webhook / geoconfig.json ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -415,7 +447,7 @@ Event raids Users with the proper access rights can choose to create event raids. These can be handy for example on raid hours and raid days. These special raid polls have event specific name, description and poll settings that need to be set in database. Example of a few settings is in ``sql/event-table-example.sql``. -``vote_key_mode`` currently supports 2 modes, 0 and 1. 0 is the standard mode where users vote for a time when they are attending. 1 is a mode with no timeslots, just a button for 'attending'. +``vote_key_mode`` currently supports two modes, 0 and 1. 0 is the standard mode where users vote for a time when they are attending. 1 is a mode with no timeslots, just a button for 'attending'. With ``time_slots`` you can set event secific time slots for vote keys when ``vote_key_mode`` 0 is selected. @@ -423,6 +455,11 @@ With ``time_slots`` you can set event secific time slots for vote keys when ``vo ``hide_raid_picture`` hides the raid picture from these event polls even if ``RAID_PICTURE`` is set to ``true``. +``pokemon_title`` select how the Pokemon name is displayed for this event. +``0`` = hide Pokemon name +``1`` = Raid boss: Kyogre +``2`` = Featured Pokemon: Kyogre + Trainer settings ---------------- @@ -430,8 +467,6 @@ The command '/trainer' allows users of the bot to change their trainer data like With ``TRAINER_CHATS`` you can specify additional chats which should appear as buttons too for sharing the trainer message. -Set ``TRAINER_BUTTONS_TOGGLE`` to true to enable the toggle which shows/hides the team and level+/- buttons under the trainer message. To disable the toggle button and always show the team and level+/- buttons set it to false. - Add additional chats -100999555111 and -100888444222 to share the trainer message ``"TRAINER_CHATS":"-100999555111,-100888444222"`` @@ -469,7 +504,7 @@ Cleanup The bot features an automatic cleanup of Telegram raid poll messages as well as cleanup of the database (attendance and raids tables). -To activate cleanup you need to `make sure your groups are Supergroups or Channels <#which-group-type-should-i-use--how-do-i-make-a-group-a-supergroup>`_\ , make your bot an admin in this chat, enable cleanup in the config and create a cronjob to trigger the cleanup process. +To activate cleanup you need to `make sure your groups are Supergroups or Channels <#referring-to-groups-channels-and-users>`_\ , make your bot an admin in this chat, enable cleanup in the config and create a cronjob to trigger the cleanup process. #. Set the ``CLEANUP`` in the config to ``true`` and define a cleanup secret/passphrase under ``CLEANUP_SECRET``. @@ -494,7 +529,7 @@ Access permissions Public access ^^^^^^^^^^^^^ -When no Telegram id, group, supergroup or channel is specified in ``BOT_ADMINS`` the bot will allow everyone to use it (public access). +When no Telegram id is specified in ``BOT_ADMINS`` the bot will allow everyone to use it (public access). Example for public access: ``"BOT_ADMINS":""`` @@ -503,11 +538,11 @@ Access and permissions The ``MAINTAINER_ID`` is not able to access the bot nor has any permissions as that id is only contacted in case of errors and issues with the bot configuration. -The ``BOT_ADMINS`` have all permissions and can use any feature of the bot. +The ``BOT_ADMINS`` have all permissions and can use any feature of the bot. No restrictions specified in access files apply to these users. Telegram Users can only vote on raid polls, but have no access to other bot functions (unless you configured it). -In order to allow Telegram chats to access the bot and use commands/features, you need to create an access file. +In order to allow members of Telegram chats to access the bot and use commands/features, you need to create an access file. It does not matter if a chat is a user, group, supergroup or channel - any kind of chat is supported as every chat has a chat id! @@ -612,7 +647,7 @@ A few examples for access files can be found below the permission overview table - Vote on shared raid poll - Not required! * - - - Create raids ``/start``\ , ``/raid`` + - Create raids ``/start`` - ``create`` * - - Create ex-raids ``/start`` @@ -625,7 +660,7 @@ A few examples for access files can be found below the permission overview table - ``raid-duration`` * - - List all raids ``/list`` and ``/listall`` - - ``list`` + - ``list`` and ``listall`` * - - Manage overview ``/overview`` - ``overview`` @@ -672,20 +707,23 @@ A few examples for access files can be found below the permission overview table - Edit extended gym details ``/gym`` - ``gym-edit`` * - - - Edit gym name ``/gymname`` - - ``gym-name`` + - Delete a gym ``/gym`` + - ``gym-delete`` * - - - Edit gym address ``/gymaddress`` - - ``gym-address`` + - Add a gym ``/gym`` + - ``gym-add`` * - - - Edit gym gps coordinates ``/gymgps`` - - ``gym-gps`` + - Edit gym name after creating a gym with ``RAID_VIA_LOCATION`` + - ``gym-name`` * - - - Edit gym note ``/gymnote`` - - ``gym-note`` + - + - + * - Settings + - Read the value of a specific setting in bot config ``/get`` + - ``config-get`` * - - - Add a gym ``/addgym`` - - ``gym-add`` + - Set the value of a specific setting in bot config ``/set`` + - ``config-set`` * - - - @@ -708,7 +746,7 @@ A few examples for access files can be found below the permission overview table - - * - Pokedex - - Manage raid pokemon ``/pokedex`` + - Manage raid Pokemon ``/pokedex`` - ``pokedex`` * - - @@ -719,6 +757,12 @@ A few examples for access files can be found below the permission overview table * - - - + * - Events + - Show help ``/events`` + - ``event-manage`` + * - + - + - * - Tutorial - Allow users to access tutorial - ``tutorial`` @@ -771,7 +815,7 @@ To enable this feature: * Create ``tutorial.php`` in config folder. Use ``tutorial.php.example`` as reference * Set ``TUTORIAL_MODE`` to ``true`` in ``config.json`` * ``tutorial`` in access config file(s) -* ``force-tutorial`` in access config file(s) to force users to go through the tutorial before they're able to use the bot. +* ``force-tutorial`` in access config file(s) to force users to go through the tutorial before they're able to use the bot. Does not apply to users specified in ``BOT_ADMINS``. Customization ------------- @@ -803,7 +847,7 @@ To change translations you can do the following: * Create a file named ``language.json`` in the custom folder -* Find the translation name/id by searching the core and bot language.php files (\ ``core/lang/language.php`` and ``lang/language.php``\ ) +* Find the translation name/id by searching the bot language.json files (\ ``lang/*.json``\ ) * Set your own translation in your custom language.json * For example to change the translation of 'Friday' to a shorter 'Fri' put the following in your ``custom/language.json``\ : @@ -842,8 +886,6 @@ Config reference - One letter ID for the bot used in debug logging. Mostly useful if you run multiple. * - BOT_NAME - Name of the bot. - * - BRIDGE_MODE - - Bool, whether to enable bridge mode. * - CLEANUP_DATABASE - Bool, whether to clean up finished raids from DB if cleanup is enabled. * - CLEANUP_LOG @@ -903,13 +945,15 @@ Config reference * - MAINTAINER - Name of main maintainer * - AUTO_REFRESH_POLLS - - Bool, enable the auto refresh feature and hides the refresh button from polls. Requires a curl job for refreshing. + - Bool, enable the auto refresh feature and hides the refresh button from polls. Requires a curl job for refreshing. * - MAPS_API_KEY - Google Maps API key for ``MAPS_LOOKUP`` * - MAPS_LOOKUP - Boolean, resolve missing gym addresses via Google Maps * - OSM_LOOKUP - Boolean, resolve missing gym addresses via OpenStreetMap + * - OSM_URL + - String, if OSM lookup is enabled, you can set private server address here. e.g. ``http://localhost:8090`` * - MAP_URL - URL to your map. This is displayed under every raid poll. * - CUSTOM_TRAINERNAME @@ -942,6 +986,10 @@ Config reference - In minutes the maximum length of the egg phase a user is allowed to give. * - RAID_EXCLUDE_EXRAID_DUPLICATION - Bool, true to exclude ex-raids from the duplication check which allows to create an ex-raid and a normal raid at the same gym + * - RAID_EXCLUDE_EVENT_DUPLICATION + - Bool, true to exclude event raids from the duplication check which allows to create an event and a normal raid at the same gym + * - RAID_EXCLUDE_ELITE_DUPLICATION + - Bool, true to exclude elite raids from the duplication check which allows to create an elite raid and a normal raid at the same gym * - RAID_EX_GYM_MARKER - Enum, "icon" (default value, a star icon) or a custom text/icon to indicate an ex-raid gym in the raid polls * - RAID_FIRST_START @@ -986,6 +1034,14 @@ Config reference - Fully qualified HTTPS URL to ``raidpicture.php``\ , for example ``https://example.com/raidbot/raidpicture.php`` * - RAID_PIN_MESSAGE - Custom message added to the bottom of the raid overview messages + * - RAID_BOSS_LIST" + - Bool, adds a list of saved raid bosses to overview message + * - RAID_BOSS_LIST_TITLE + - String, title of the list + * - RAID_BOSS_LIST_RAID_LEVELS + - Array, list of raid levels included in the list + * - RAID_BOSS_LIST_ROW_LIMIT + - Int, limit the list to set number of rows * - RAID_POLL_HIDE_BUTTONS_POKEMON - List of Pokemon dex IDs for which voting buttons are disabled * - RAID_POLL_HIDE_BUTTONS_RAID_LEVEL @@ -1048,8 +1104,6 @@ Config reference - Timezone definition to use as per `TZ database names `_ * - TRAINER_MAX_LEVEL - Int, Maximum level a trainer can be (currently 50) - * - TRAINER_BUTTONS_TOGGLE - - Bool, true to show/hide the team and level+/- buttons below the trainer data setup messages once a users hits the "trainer info" button. False to always show the team and level+/- buttons. * - TRAINER_CHATS - List of chats where trainer data setup messages can be shared * - UPGRADE_SQL_AUTO @@ -1074,6 +1128,8 @@ Config reference - List of Telegram chat IDs to autoshare raids of level 5 * - WEBHOOK_CHATS_ALL_LEVELS - List of Telegram chat IDs to autoshare raids of any level + * - WEBHOOK_CHATS_BY_POKEMON + - Automatically share only specific Pokemon to set chats. See `Raids from Webhook`_ for further details. * - WEBHOOK_CREATE_ONLY - Bool, only create raids, don't autoshare them to any chat * - WEBHOOK_CREATOR diff --git a/docs/development.rst b/docs/development.rst index dd3da3a9..08aee9f4 100644 --- a/docs/development.rst +++ b/docs/development.rst @@ -9,6 +9,12 @@ Adding new config values * You can access the new config item in code with ``$config->CONFIG_ITEM_NAME`` but if inside a function, remember to specify ``global $config;`` * Don't break backwards compatibility if you can. +Formatting callback data in inline keyboards +-------------------------------------------- + +Old way of sending data over key presses was ``id:action:arg``. These values got stored early on in ``$data`` array. This is still supported for as long as this method is used in the code somewhere, but we'll try to move to the new system overtime. +New data format adds variable names to passed data. When forming keyboard arrays you should use the function ``formatCallbackData()`` to convert your array to string. + Adding new metrics ------------------ @@ -32,7 +38,7 @@ the schema version is final and immutable and any schema changes need to happen Translations ------------ -Translations are stored in ``lang/language.json`` and ``core/lang/language.json``. Any string marked as ``TRANSLATE`` hasn't been translated yet. These can be changed by hand but if you want to add a new language or do large scale translation, using translate.py is recommended. +Translations are stored in ``lang/language.json``. Any string marked as ``TRANSLATE`` hasn't been translated yet. These can be changed by hand but if you want to add a new language or do large scale translation, using translate.py is recommended. translate.py ^^^^^^^^^^^^ diff --git a/docs/index.rst b/docs/index.rst index ff768754..ebad472f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -52,6 +52,7 @@ You can submit translations to expand our list of lanuages! For more information * DE (German) * EN (English) +* ES (Spanish) * FI (Finnish) * FR (French) * IT (Italian) diff --git a/docs/maintenance.rst b/docs/maintenance.rst index 92195ff1..130c372a 100644 --- a/docs/maintenance.rst +++ b/docs/maintenance.rst @@ -28,7 +28,7 @@ To automatically keep the raid boss data somewhat up to date, you can schedule t ``curl -k -d '{"callback_query":{"data":"LEVELS:update_bosses:SOURCE"}}' https://localhost/botdir/index.php?apikey=111111111:AABBccddEEFFggHHiijjKKLLmmnn`` -Currently supported arguments for LEVELS are raid levels ``1, 3, 5, 6`` in comma separated string, and ``scheduled`` to execute import of scheduled info for tier 5 and 6 raids. +Currently supported arguments for LEVELS are raid levels ``1, 3, 5, 6`` in comma separated string, and ``scheduled`` to execute import of scheduled info for tier 5 and higher raids. Currently supported arguments for SOURCE are ``pogoinfo``, which is only used when importing specific levels. diff --git a/docs/manual_install.rst b/docs/manual_install.rst index f6edcf12..57333d5e 100644 --- a/docs/manual_install.rst +++ b/docs/manual_install.rst @@ -9,7 +9,7 @@ Webserver requirements Preferably: * Apache2 -* PHP7 +* PHP8.1 * MySQL5 or MariaDB10. MariaDB is preferred, MySQL8 will cause some warnings. * Curl * SSL Certificate ( https://www.letsencrypt.org ) diff --git a/docs/usage.rst b/docs/usage.rst index f9d1138c..6cd017c7 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -4,11 +4,11 @@ PokemonRaidBot usage Send your location to the bot ----------------------------- -If ``RAID_LOCATION`` is set to ``true`` (default), the bot will guide you through the creation of a raid poll based on the settings in the config file. +If ``RAID_LOCATION`` is set to ``true`` (default), the bot will guide you through the creation of a raid poll based on the settings in the config file. ``RAID_VIA_LOCATION_FUNCTION`` determines the actions taken after location is received. -In case of a raid poll the bot will ask you for the raid level, the pokemon raid boss, the time until the raids starts and the time left for the raid. Afterwards you can set the gym name and gym team by using the /gym and /team commands. +By default the bot will ask you for the raid level, the pokemon raid boss, the time until the raids starts and the time left for the raid. Afterwards you can set the gym name by using the /gym or /gymname commands. -If ``LIST_BY_LOCATION`` is set to ``true``\ , the bot will instead list all nearby saved raids. +For further details please refer to :doc:`config#raid-creation-options` Using inline search of @PortalMapBot or @Ingressportalbot --------------------------------------------------------- @@ -264,17 +264,22 @@ Delete an existing raid poll with the ``/delete`` command: :alt: Command: /delete -Command: /team -^^^^^^^^^^^^^^ +Command: /trainer +^^^^^^^^^^^^^^^^^ -The bot will set the team to Mystic/Valor/Instinct for the last created raid based on your input. +Users can use this command to set their trainer name, friend code, team, level and if configured, personal bot settings (private language and automatic raid alarms). -Example input: ``/team Mystic`` +For users with proper access rights the bot will also give you a list of chats to share the trainer message which allows users to set team and level+/- data. You can also delete the shared trainer messages via the ``/trainer`` command. -Command: /trainer +Command: /history ^^^^^^^^^^^^^^^^^ -The bot will give you a list of chats to share the trainer message which allows users to set team and level+/- data. You can also delete the shared trainer messages via the ``/trainer`` command. +Tool for admins to view history of raids that had at least one person signed up for it. + +Command: /events +^^^^^^^^^^^^^^^^ + +Tool for admins to edit raid events. The UI is very simple and for some stuff you need to refer to this documentation. Command: /gym ^^^^^^^^^^^^^ @@ -283,13 +288,6 @@ The bot will show the details of each gym. Additionally you can change the exten Example input: ``/gym`` -Command: /addgym -^^^^^^^^^^^^^^^^ - -The bot will add a gym under the coordinates you're submitting. First latitude, then longitude. The gym is added under the name '#YourTelegramID' (e.g. '#111555777') and you need to change the name afterwards using the ``/gymname`` command. You cannot submit a second gym unless you changed the name of the first gym. In case you submit a second gym without changing the name of the previously submitted gym, the first gym coordinates will be overwritten! - -Example input: ``/addgym 52.5145434,13.3501189`` - Command: /gymname ^^^^^^^^^^^^^^^^^ @@ -298,28 +296,3 @@ The bot will set the name of gym to your input. If you submitted a gym via locat Example input: ``/gymname Siegessäule`` Example input with gym id: ``/gymname 34, Siegessäule`` - -Command: /gymaddress -^^^^^^^^^^^^^^^^^^^^ - -The bot will set the address of gym to your input. The id of the gym is required. You can delete the gym address using the keyword 'reset'. - -Example input: ``/gymaddress 34, Großer Stern, 10557 Berlin`` - -Example input to delete the gym address: ``/gymaddress 34, reset`` - -Command: /gymgps -^^^^^^^^^^^^^^^^ - -The bot will set the gps coordinates of gym to your input. The id of the gym is required. - -Example input: ``/gymgps 34, 52.5145434,13.3501189`` - -Command: /gymnote -^^^^^^^^^^^^^^^^^ - -The bot will set the note for gym to your input. The id of the gym is required. You can delete the gym note using the keyword 'reset'. - -Example input: ``/gymnote 34, Meeting point: Behind the building`` - -Example input to delete the gym note: ``/gymnote 34, reset`` diff --git a/getPokemonIcons.php b/getPokemonIcons.php index 6037f62f..de4109ec 100644 --- a/getPokemonIcons.php +++ b/getPokemonIcons.php @@ -10,15 +10,15 @@ $repos = []; $repos[] = array('owner' => "PokeMiners", - 'name' => "pogo_assets", - 'branch' => "master", - 'subdir' => "", - 'dir' => "Images/Pokemon - 256x256"); + 'name' => "pogo_assets", + 'branch' => "master", + 'subdir' => "", + 'dir' => "Images/Pokemon - 256x256"); $repos[] = array('owner' => "PokeMiners", - 'name' => "pogo_assets", - 'branch' => "master", - 'subdir' => "Addressable_Assets/", - 'dir' => "Images/Pokemon - 256x256/Addressable Assets"); + 'name' => "pogo_assets", + 'branch' => "master", + 'subdir' => "", + 'dir' => "Images/Pokemon - 256x256/Addressable Assets"); // Get download function curl_get_contents include('logic/curl_get_contents.php'); @@ -46,152 +46,152 @@ function is_updated($path, $file_object) { // Process each repo foreach ($repos as $key => $r) { - $repo_owner = $r['owner']; - $repo_name = $r['name']; - $repo_branch = $r['branch']; - $repo_dir = $r['dir']; - $dest = $destination . 'pokemon_' . $repo_owner . '/' . $r['subdir']; - - // Set destination to different path - if(isset($options['dir'])) { - $dest = rtrim($options['dir'],"/") . '/'; - } + $repo_owner = $r['owner']; + $repo_name = $r['name']; + $repo_branch = $r['branch']; + $repo_dir = $r['dir']; + $dest = $destination . 'pokemon_' . $repo_owner . '/' . $r['subdir']; + + // Set destination to different path + if(isset($options['dir'])) { + $dest = rtrim($options['dir'],"/") . '/'; + } - // Make sure destination exists otherwise create it - if (!file_exists($dest)) { - mkdir($dest); - } + // Make sure destination exists otherwise create it + if (!file_exists($dest)) { + mkdir($dest); + } - // Content dir - $content_dir = ''; - if (strpos($repo_dir, '/') !== false) { - $content_dir = str_replace(' ', '%20',substr($repo_dir, 0, strrpos($repo_dir, '/'))); - } - // Raw download dir - $raw_dir = $repo_dir; - if (strpos($repo_dir, ' ') !== false) { - $raw_dir = str_replace(' ', '%20', $repo_dir); - } + // Content dir + $content_dir = ''; + if (strpos($repo_dir, '/') !== false) { + $content_dir = str_replace(' ', '%20',substr($repo_dir, 0, strrpos($repo_dir, '/'))); + } + // Raw download dir + $raw_dir = $repo_dir; + if (strpos($repo_dir, ' ') !== false) { + $raw_dir = str_replace(' ', '%20', $repo_dir); + } - // Git urls - $repo_content = 'https://api.github.com/repos/' . $repo_owner . '/' . $repo_name . '/contents/' . $content_dir; - $repo_html = 'https://github.com/' . $repo_owner . '/' . $repo_name . '/' . $repo_dir . '/'; - $repo_raw = 'https://raw.githubusercontent.com/' . $repo_owner . '/' . $repo_name . '/' . $repo_branch . '/' . $raw_dir . '/'; - - // Git tree lookup - $tree = curl_get_contents($repo_content); - $leaf = json_decode($tree, true); - // Detect rate-limiting and die gracefully - if(is_array($leaf) && in_array('message', $leaf)) { - die('Failed to download repo index: ' . $leaf['message']); - } + // Git urls + $repo_content = 'https://api.github.com/repos/' . $repo_owner . '/' . $repo_name . '/contents/' . $content_dir; + $repo_html = 'https://github.com/' . $repo_owner . '/' . $repo_name . '/' . $repo_dir . '/'; + $repo_raw = 'https://raw.githubusercontent.com/' . $repo_owner . '/' . $repo_name . '/' . $repo_branch . '/' . $raw_dir . '/'; + + // Git tree lookup + $tree = curl_get_contents($repo_content); + $leaf = json_decode($tree, true); + // Detect rate-limiting and die gracefully + if(is_array($leaf) && in_array('message', $leaf)) { + die('Failed to download repo index: ' . $leaf['message']); + } - // Debug - //echo 'LEAF:' . PHP_EOL; - //print_r($leaf) . PHP_EOL; - - // Git tree lookup for repo dir - $content = ''; - $foldername = basename($repo_html); - echo 'Downloading each file from ' . $repo_html . PHP_EOL; - foreach ($leaf as $l) { - if($l['name'] == $foldername && $l['type'] == 'dir') { - $json = curl_get_contents($l['git_url']); - $content = json_decode($json, true); - break; - } + // Debug + //echo 'LEAF:' . PHP_EOL; + //print_r($leaf) . PHP_EOL; + + // Git tree lookup for repo dir + $content = ''; + $foldername = basename($repo_html); + echo 'Downloading each file from ' . $repo_html . PHP_EOL; + foreach ($leaf as $l) { + if($l['name'] == $foldername && $l['type'] == 'dir') { + $json = curl_get_contents($l['git_url']); + $content = json_decode($json, true); + break; } + } - // Download each file. - if(is_array($content)) { - $count_unchanged = 0; - $count_extension = 0; - $i = 0; - $successfull_count = 0; - $files_downloaded = 0; - $chunk_start = 0; - $multi_handle = curl_multi_init(); - $treecount = count($content['tree']); - // Divide downloadable content into chunks. Download 20 files at a time by default - if(isset($options['chunk']) && is_integer($options['chunk'])) $file_chunk = $options['chunk']; else $file_chunk = 20; - $chunk_end = floor($treecount/$file_chunk); - for( $p=$chunk_start; $p < $chunk_end; $p++ ) { - $file_pointers = []; - $curl_handles = []; - $output_info = []; - $start = $p*$file_chunk; - $handles = 0; - if( ($start+$file_chunk) > $treecount) $end = $treecount-$start; else $end = $start+$file_chunk; - for( $i=$start; $i < $end; $i++) { - $c = $content['tree'][$i]; - - // Filter by file extension - $ext = '.' . pathinfo($c['path'], PATHINFO_EXTENSION); - if($filter == $ext) { - // Only get files that don't exist or where the hash doesn't match - if(is_updated($dest . $c['path'], $c)) { - echo 'Downloading ' . $c['path'] . PHP_EOL; - $input = $repo_raw . $c['path']; - $output = $dest . $c['path']; - - $output_info[$handles] = ['file'=>$output, 'source_size'=>$c['size']]; - $curl_handles[$handles] = curl_init($input); - $file_pointers[$handles] = fopen($output, 'w'); - curl_setopt($curl_handles[$handles], CURLOPT_FILE, $file_pointers[$handles]); - curl_setopt($curl_handles[$handles], CURLOPT_HEADER, 0); - curl_setopt($curl_handles[$handles], CURLOPT_CONNECTTIMEOUT, 60); - curl_multi_add_handle($multi_handle,$curl_handles[$handles]); - $handles++; - } else { - $count_unchanged = $count_unchanged + 1; - // Debug - // echo 'Skipping file: ' . $c['path'] . " (File hasn't changed.)" . PHP_EOL; - } - } else { - $count_extension = $count_extension + 1; - // Debug - // echo 'Skipping file: ' . $c['path'] . ' (File extension filtering)' . PHP_EOL; - } - } - // Download the files - do { - curl_multi_exec($multi_handle,$running); - } while ($running > 0); - - for($o=0;$o<$handles;$o++) { - curl_multi_remove_handle($multi_handle, $curl_handles[$o]); - curl_close($curl_handles[$o]); - fclose ($file_pointers[$o]); - $files_downloaded++; - // Verify download - // File successfully created? - if(!is_file($output_info[$o]['file'])) { - echo 'Error downloading file, no output file was found: ' . $output_info[$o]['file'] . PHP_EOL; - } else { - $real_filesize = filesize($output_info[$o]['file']); - if ($real_filesize != $output_info[$o]['source_size']) { - echo "Error downloading file, size doesn't match (" . $real_filesize . " != " . $output_info[$o]['source_size'] . ")!" . PHP_EOL; - }else { - $successfull_count++; - } - } - } - } - echo $successfull_count . '/' . $files_downloaded . ' files downloaded successfully!' . PHP_EOL . PHP_EOL; - curl_multi_close($multi_handle); - // Unchanged files - if($count_unchanged > 0) { - echo 'Skipped ' . $count_unchanged . ' unchanged files' . PHP_EOL; - } - // Filtered files - if($count_extension > 0) { - echo 'Skipped ' . $count_extension . ' files due to wrong file extension'. PHP_EOL; - } - } else { - echo "Failed to download repo content!" . PHP_EOL; - echo "Repo content: " . $repo_content . PHP_EOL; + // Download each file. + if(!is_array($content)) { + echo "Failed to download repo content!" . PHP_EOL; + echo "Repo content: " . $repo_content . PHP_EOL; + continue; + } + $count_unchanged = $count_extension = $i = $successfull_count = $files_downloaded = $chunk_start = $count_naming = 0; + $multi_handle = curl_multi_init(); + $treecount = count($content['tree']); + // Divide downloadable content into chunks. Download 20 files at a time by default + if(isset($options['chunk']) && is_integer($options['chunk'])) $file_chunk = $options['chunk']; else $file_chunk = 20; + $chunk_end = floor($treecount/$file_chunk); + for( $p = $chunk_start; $p < $chunk_end; $p++ ) { + $file_pointers = $curl_handles = $output_info = []; + $start = $p*$file_chunk; + $handles = 0; + if( ($start+$file_chunk) > $treecount) $end = $treecount-$start; else $end = $start+$file_chunk; + for( $i = $start; $i < $end; $i++) { + $c = $content['tree'][$i]; + + // Filter by file extension + $ext = '.' . pathinfo($c['path'], PATHINFO_EXTENSION); + if($filter != $ext) { + $count_extension = $count_extension + 1; + // Debug + // echo 'Skipping file: ' . $c['path'] . ' (File extension filtering)' . PHP_EOL; + continue; + } + // Only get files that don't exist or where the hash doesn't match + if(!is_updated($dest . $c['path'], $c)) { + $count_unchanged = $count_unchanged + 1; + // Debug + // echo 'Skipping file: ' . $c['path'] . " (File hasn't changed.)" . PHP_EOL; + continue; + } + // Skip files that don't match the new addressable assets naming convention + if(substr($c['path'], 0, 2) != 'pm') { + $count_naming++; + continue; + } + echo 'Downloading ' . $c['path'] . PHP_EOL; + $input = $repo_raw . $c['path']; + $output = $dest . $c['path']; + + $output_info[$handles] = ['file'=>$output, 'source_size'=>$c['size']]; + $curl_handles[$handles] = curl_init($input); + $file_pointers[$handles] = fopen($output, 'w'); + curl_setopt($curl_handles[$handles], CURLOPT_FILE, $file_pointers[$handles]); + curl_setopt($curl_handles[$handles], CURLOPT_HEADER, 0); + curl_setopt($curl_handles[$handles], CURLOPT_CONNECTTIMEOUT, 60); + curl_multi_add_handle($multi_handle,$curl_handles[$handles]); + $handles++; + } + // Download the files + do { + curl_multi_exec($multi_handle,$running); + } while ($running > 0); + + for($o=0;$o<$handles;$o++) { + curl_multi_remove_handle($multi_handle, $curl_handles[$o]); + curl_close($curl_handles[$o]); + fclose ($file_pointers[$o]); + $files_downloaded++; + // Verify download + // File successfully created? + if(!is_file($output_info[$o]['file'])) { + echo 'Error downloading file, no output file was found: ' . $output_info[$o]['file'] . PHP_EOL; + continue; + } + $real_filesize = filesize($output_info[$o]['file']); + if ($real_filesize != $output_info[$o]['source_size']) { + echo "Error downloading file, size doesn't match (" . $real_filesize . " != " . $output_info[$o]['source_size'] . ")!" . PHP_EOL; + continue; + } + $successfull_count++; } + } + echo $successfull_count . '/' . $files_downloaded . ' files downloaded successfully!' . PHP_EOL . PHP_EOL; + curl_multi_close($multi_handle); + // Unchanged files + if($count_unchanged > 0) { + echo 'Skipped ' . $count_unchanged . ' unchanged files' . PHP_EOL; + } + // Filtered files + if($count_extension > 0) { + echo 'Skipped ' . $count_extension . ' files due to wrong file extension'. PHP_EOL; + } + if($count_naming > 0) { + echo 'Skipped ' . $count_naming . ' files with old file naming convention'. PHP_EOL; + } } echo "Finished pokemon image refresh." . PHP_EOL; -?> diff --git a/getTranslations.php b/getTranslations.php index dc641662..62e6cf30 100644 --- a/getTranslations.php +++ b/getTranslations.php @@ -1,112 +1,75 @@ ["PT-BR"], - "English" => ["EN"], - "French" => ["FR"], - "German" => ["DE"], - "Italian" => ["IT"], - "Russian" => ["RU"], - "Spanish" => ["ES"], - ]; -$move_translations_to_fetch = [ - "BrazilianPortuguese" => ["PT-BR"], - "English" => ["EN", "FI", "NL", "NO", "PL"], - "French" => ["FR"], - "German" => ["DE"], - "Italian" => ["IT"], - "Russian" => ["RU"], - "Spanish" => ["ES"], - ]; +$translations_to_fetch = [ + 'pt-br' => 'PT-BR', + 'en' => 'EN', + 'fr' => 'FR', + 'de' => 'DE', + 'it' => 'IT', + 'ru' => 'RU', + 'es' => 'ES', +]; // Initialize array -$move_array = []; -$pokemon_array = []; +$move_array = $pokemon_array = $form_array = []; -// Loop through all available translations -foreach($translations_available as $language) { - $write_pokemon_translation = array_key_exists($language, $pokemon_translations_to_fetch); - $write_move_translation = array_key_exists($language, $move_translations_to_fetch); - - // Only read the file if a translation is wanted - if( $write_pokemon_translation || $write_move_translation ) { - // Open the file(s) and write it into an array - foreach($lang_directory_url as $url) { - $file = curl_open_file($url . $language . '.txt'); - $data = explode("\n", $file); - - // Read the file line by line - foreach($data as $row) { - // Handle resource ID rows - if(substr($row, 0, 1) == 'R') { - $resource_id = substr(trim($row), 13); - $resource_part = explode("_",$resource_id); - } - // Handle text rows - if(substr($row, 0, 1) == 'T') { - $text = substr(trim($row), 6); - - // Filter out mega translations - if(count($resource_part) == 3 && $resource_part[1] == 'name') { - $id = intval($resource_part[2]); // remove leading zeroes - // Save pokemon names into an array if pokemon id is larger than 0 - if($write_pokemon_translation && $resource_part[0] == 'pokemon' && $id > 0) { - foreach($pokemon_translations_to_fetch[$language] as $lan) { - $pokemon_array['pokemon_id_'.$id][$lan] = $text; - } - // Save pokemon moves into an array - }elseif($write_move_translation && $resource_part[0] == 'move') { - foreach($move_translations_to_fetch[$language] as $lan) { - $move_array['pokemon_move_'.$id][$lan] = $text; - } - } - } - } - } - unset($file); - unset($data); - } +// Loop through translations +foreach($translations_to_fetch as $lanfile => $language) { + $file = curl_open_file('https://raw.githubusercontent.com/WatWowMap/pogo-translations/master/static/locales/'. $lanfile. '.json'); + $translationData = json_decode($file, true); + foreach($translationData as $title => $translation) { + $split = explode('_', $title); + if(count($split) != 2 or intval($split[1]) <= 0) continue; + [$key, $id] = $split; + // Save pokemon names into an array if pokemon id is larger than 0 + if($key == 'poke') { + $pokemon_array['pokemon_id_'.$id][$language] = $translation; + // Save pokemon moves into an array + }elseif($key == 'move') { + $move_array['pokemon_move_'.$id][$language] = $translation; + }elseif($key == 'form') { + $form_array['pokemon_form_'. $id][$language] = $translation; } + } } + // Bot defaults to using english translations, so no need to add duplicates for every language $pokemon_array = remove_duplicate_translations($pokemon_array); +$move_array = remove_duplicate_translations($move_array); +$form_array = remove_duplicate_translations($form_array); -// Build the path to move translation file -$moves_translation_file = $core_lang_dir . 'pokemon_moves.json'; +$pokemon_translation_file = $lang_dir . 'pokemon.json'; +$moves_translation_file = $lang_dir . 'pokemon_moves.json'; +$forms_translation_file = $lang_dir . 'pokemon_forms.json'; -// Save translations to the file -file_put_contents($moves_translation_file, json_encode($move_array,JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), LOCK_EX); - -// Build the path to translation file -$pokemon_translation_file = $core_lang_dir . 'pokemon.json'; - -// Save translations to file file_put_contents($pokemon_translation_file, json_encode($pokemon_array,JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), LOCK_EX); +file_put_contents($moves_translation_file, json_encode($move_array,JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), LOCK_EX); +file_put_contents($forms_translation_file, json_encode($form_array,JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), LOCK_EX); function curl_open_file($input) { - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $input); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - $data = curl_exec($ch); - curl_close ($ch); - return $data; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $input); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + $data = curl_exec($ch); + curl_close ($ch); + return $data; } function remove_duplicate_translations($array) { - $new_array = []; - foreach($array as $translation_id => $translations) { - foreach($translations as $lang => $translation) { - if($lang == 'EN' or $translation != $array[$translation_id]['EN']) - $new_array[$translation_id][$lang] = $translation; - } + $new_array = []; + foreach($array as $translation_id => $translations) { + foreach($translations as $lang => $translation) { + if( + ($lang == 'EN' or (isset($array[$translation_id]['EN']) && $translation != $array[$translation_id]['EN'])) and + !in_array($array[$translation_id]['EN'], ['Normal','Purified','Shadow']) + ) + $new_array[$translation_id][$lang] = $translation; } - return $new_array; + } + return $new_array; } -?> \ No newline at end of file diff --git a/images/exgym.png b/images/exgym.png deleted file mode 100644 index 18f2c229..00000000 Binary files a/images/exgym.png and /dev/null differ diff --git a/images/raid_eggs/pokemon_icon_99910_00.png b/images/raid_eggs/pokemon_icon_99910_00.png new file mode 100644 index 00000000..db453067 Binary files /dev/null and b/images/raid_eggs/pokemon_icon_99910_00.png differ diff --git a/images/raid_eggs/pokemon_icon_99911_00.png b/images/raid_eggs/pokemon_icon_99911_00.png new file mode 100644 index 00000000..5833cdad Binary files /dev/null and b/images/raid_eggs/pokemon_icon_99911_00.png differ diff --git a/images/raid_eggs/pokemon_icon_99913_00.png b/images/raid_eggs/pokemon_icon_99913_00.png new file mode 100644 index 00000000..968a4c2a Binary files /dev/null and b/images/raid_eggs/pokemon_icon_99913_00.png differ diff --git a/images/raid_eggs/pokemon_icon_99915_00.png b/images/raid_eggs/pokemon_icon_99915_00.png new file mode 100644 index 00000000..6e48ded6 Binary files /dev/null and b/images/raid_eggs/pokemon_icon_99915_00.png differ diff --git a/images/raid_eggs/pokemon_icon_99916_00.png b/images/raid_eggs/pokemon_icon_99916_00.png new file mode 100644 index 00000000..c5efafa7 Binary files /dev/null and b/images/raid_eggs/pokemon_icon_99916_00.png differ diff --git a/images/raid_eggs/pokemon_icon_9994_00.png b/images/raid_eggs/pokemon_icon_9994_00.png index 747b9d57..0e36e215 100644 Binary files a/images/raid_eggs/pokemon_icon_9994_00.png and b/images/raid_eggs/pokemon_icon_9994_00.png differ diff --git a/images/raid_eggs/pokemon_icon_9997_00.png b/images/raid_eggs/pokemon_icon_9997_00.png new file mode 100644 index 00000000..bdd08eed Binary files /dev/null and b/images/raid_eggs/pokemon_icon_9997_00.png differ diff --git a/images/raid_eggs/pokemon_icon_9998_00.png b/images/raid_eggs/pokemon_icon_9998_00.png new file mode 100644 index 00000000..8df11e88 Binary files /dev/null and b/images/raid_eggs/pokemon_icon_9998_00.png differ diff --git a/images/raid_eggs/pokemon_icon_9999_00.png b/images/raid_eggs/pokemon_icon_9999_00.png new file mode 100644 index 00000000..5146a1e2 Binary files /dev/null and b/images/raid_eggs/pokemon_icon_9999_00.png differ diff --git a/images/shadow.png b/images/shadow.png new file mode 100644 index 00000000..cb12a540 Binary files /dev/null and b/images/shadow.png differ diff --git a/images/types/normal.png b/images/types/1.png similarity index 100% rename from images/types/normal.png rename to images/types/1.png diff --git a/images/types/fire.png b/images/types/10.png similarity index 100% rename from images/types/fire.png rename to images/types/10.png diff --git a/images/types/water.png b/images/types/11.png similarity index 100% rename from images/types/water.png rename to images/types/11.png diff --git a/images/types/grass.png b/images/types/12.png similarity index 100% rename from images/types/grass.png rename to images/types/12.png diff --git a/images/types/electric.png b/images/types/13.png similarity index 100% rename from images/types/electric.png rename to images/types/13.png diff --git a/images/types/psychic.png b/images/types/14.png similarity index 100% rename from images/types/psychic.png rename to images/types/14.png diff --git a/images/types/ice.png b/images/types/15.png similarity index 100% rename from images/types/ice.png rename to images/types/15.png diff --git a/images/types/dragon.png b/images/types/16.png similarity index 100% rename from images/types/dragon.png rename to images/types/16.png diff --git a/images/types/dark.png b/images/types/17.png similarity index 100% rename from images/types/dark.png rename to images/types/17.png diff --git a/images/types/fairy.png b/images/types/18.png similarity index 100% rename from images/types/fairy.png rename to images/types/18.png diff --git a/images/types/fighting.png b/images/types/2.png similarity index 100% rename from images/types/fighting.png rename to images/types/2.png diff --git a/images/types/flying.png b/images/types/3.png similarity index 100% rename from images/types/flying.png rename to images/types/3.png diff --git a/images/types/poison.png b/images/types/4.png similarity index 100% rename from images/types/poison.png rename to images/types/4.png diff --git a/images/types/ground.png b/images/types/5.png similarity index 100% rename from images/types/ground.png rename to images/types/5.png diff --git a/images/types/rock.png b/images/types/6.png similarity index 100% rename from images/types/rock.png rename to images/types/6.png diff --git a/images/types/bug.png b/images/types/7.png similarity index 100% rename from images/types/bug.png rename to images/types/7.png diff --git a/images/types/ghost.png b/images/types/8.png similarity index 100% rename from images/types/ghost.png rename to images/types/8.png diff --git a/images/types/steel.png b/images/types/9.png similarity index 100% rename from images/types/steel.png rename to images/types/9.png diff --git a/index.php b/index.php index 49fa1cea..981a6ee5 100644 --- a/index.php +++ b/index.php @@ -7,6 +7,8 @@ $requests_total->inc(['/']); } +$botUser = new botUser; + // Start logging. debug_log("RAID-BOT '" . $config->BOT_ID . "'"); @@ -20,119 +22,126 @@ if (isset($raid['type']) && $raid['type'] == 'raid') { // Create raid(s) and exit. include_once(ROOT_PATH . '/commands/raid_from_webhook.php'); - $dbh = null; exit(); } } } + +$update['type'] = NULL; +if(isset($update['message'])) { + $update['type'] = 'message'; +} else if(isset($update['callback_query'])) { + $update['type'] = 'callback_query'; +} else if(isset($update['inline_query'])) { + $update['type'] = 'inline_query'; +} else if(isset($update['channel_post'])) { + $update['type'] = 'channel_post'; +} + // Init empty data array. $data = []; // Callback data found. if (isset($update['callback_query']['data'])) { - // Bridge mode? - if($config->BRIDGE_MODE) { - // Split bot folder name away from actual data. - $botnameData = explode(':', $update['callback_query']['data'], 2); - $botname = $botnameData[0]; - $thedata = $botnameData[1]; - // Write to log - debug_log('Bot Name: ' . $botname); - debug_log('The Data: ' . $thedata); - $botname_length = count(str_split($botname)); - if($botname_length > 8) { - info_log("ERROR! Botname '" . $botname . "' is too long, max: 8","!"); - exit(); - } - } else { - // Data is just the data. - $thedata = $update['callback_query']['data']; + $thedata = $update['callback_query']['data']; + $splitDataOld = explode(':', $thedata); + // Split callback data and assign to data array. + if(count($splitDataOld) > 2) { + $data['id'] = $splitDataOld[0]; + $data[0] = $splitDataOld[1]; + $data['arg'] = $splitDataOld[2]; + }else { + $splitData = explode('|', $thedata); + $data[0] = $splitData[0]; + unset($splitData[0]); + foreach($splitData as $dataPiece) { + [$key, $value] = explode('=', $dataPiece, 2); + $data[$key] = $value; } - // Split callback data and assign to data array. - $splitData = explode(':', $thedata); - $data['id'] = $splitData[0]; - $data['action'] = $splitData[1]; - $data['arg'] = $splitData[2]; + } +} + +// Run cleanup if requested +if (isset($update['cleanup'])) { + include_once(CORE_BOT_PATH . '/cleanup_run.php'); + cleanup_auth_and_run($update); + exit; } // DDOS protection if($config->ENABLE_DDOS_PROTECTION) { - include_once(CORE_BOT_PATH . '/ddos.php'); + include_once(CORE_BOT_PATH . '/ddos.php'); } +if(isset($update[$update['type']]['from'])) { // Update the user -update_user($update); + $botUser->updateUser($update); -// Get language -include_once(CORE_BOT_PATH . '/userlanguage.php'); + // Get language + $botUser->defineUserLanguage($update); -// Run cleanup if requested -if (isset($update['cleanup'])) { - include_once(CORE_BOT_PATH . '/cleanup_run.php'); - cleanup_auth_and_run($update); + $botUser->initPrivileges(); } // Callback query received. if (isset($update['callback_query'])) { - // Logic to get the module - include_once(CORE_BOT_PATH . '/modules.php'); + // Logic to get the module + include_once(CORE_BOT_PATH . '/modules.php'); // Inline query received. } else if (isset($update['inline_query'])) { - // List quests and exit. - raid_list($update); - $dbh = null; - exit(); + include_once(LOGIC_PATH . '/raid_list.php'); + // List raids and exit. + raid_list($update); + exit(); // Location received. } else if (isset($update['message']['location']) && $update['message']['chat']['type'] == 'private') { - if($config->RAID_VIA_LOCATION_FUNCTION == 'list') { - include_once(ROOT_PATH . '/mods/share_raid_by_location.php'); - }else { - // Create raid and exit. - include_once(ROOT_PATH . '/mods/raid_by_location.php'); - } - $dbh = null; - exit(); + if($config->RAID_VIA_LOCATION_FUNCTION == 'list') { + include_once(ROOT_PATH . '/mods/share_raid_by_location.php'); + }else { + // Create raid and exit. + include_once(ROOT_PATH . '/mods/raid_by_location.php'); + } + exit(); // Cleanup collection from channel/supergroup messages. -} else if ((isset($update['channel_post']) && $update['channel_post']['chat']['type'] == "channel") || (isset($update['message']) && $update['message']['chat']['type'] == "supergroup")) { - // Collect cleanup information - include_once(CORE_BOT_PATH . '/cleanup_collect.php'); +} else if (isset($update[$update['type']]) && in_array($update[$update['type']]['chat']['type'], ['channel', 'supergroup'])) { + // Collect cleanup information + include_once(CORE_BOT_PATH . '/cleanup_collect.php'); // Message is required to check for commands. -} else if (isset($update['message']) && ($update['message']['chat']['type'] == 'private' || $update['message']['chat']['type'] == 'channel')) { - // Portal message? - if(isset($update['message']['entities']['1']['type']) && $update['message']['entities']['1']['type'] == 'text_link' && strpos($update['message']['entities']['1']['url'], 'https://intel.ingress.com/intel?ll=') === 0) { - // Import portal. - include_once(ROOT_PATH . '/mods/importal.php'); - } else { - // Check if user is expected to be posting something we want to save to db - if($update['message']['chat']['type'] == 'private') { - $q = my_query("SELECT id, handler, modifiers FROM user_input WHERE user_id='{$update['message']['from']['id']}' LIMIT 1"); - if( $q->rowCount() > 0 ) { - debug_log("Expecting a response message from user: " . $update['message']['from']['id']); - $res = $q->fetch(); - // Modifiers to pass to handler - $modifiers = json_decode($res['modifiers'], true); - - debug_log("Calling: " . $res['handler'] . '.php'); - debug_log("With modifiers: " . $res['modifiers']); - include_once(ROOT_PATH . '/mods/' . $res['handler'] . '.php'); - - debug_log("Response handeled successfully!"); - // Delete the entry if the call was handled without errors - my_query("DELETE FROM user_input WHERE id='{$res['id']}'"); - - $dbh = null; - exit(); - } - } - - // Logic to get the command - include_once(CORE_BOT_PATH . '/commands.php'); +} else if (isset($update['message']) && $update['message']['chat']['type'] == 'private') { + // Update user's privileges into database + $botUser->privilegeCheck(); + + // Portal message? + if(isset($update['message']['entities']['1']['type']) && $update['message']['entities']['1']['type'] == 'text_link' && strpos($update['message']['entities']['1']['url'], 'https://intel.ingress.com/intel?ll=') === 0) { + // Import portal. + include_once(ROOT_PATH . '/mods/importal.php'); + exit; + } + // Check if user is expected to be posting something we want to save to db + if($update['message']['chat']['type'] == 'private') { + $q = my_query('SELECT id, handler, modifiers FROM user_input WHERE user_id = ? LIMIT 1', [$update['message']['from']['id']]); + if( $q->rowCount() > 0 ) { + debug_log('Expecting a response message from user: ' . $update['message']['from']['id']); + $res = $q->fetch(); + // Modifiers to pass to handler + $modifiers = json_decode($res['modifiers'], true); + + debug_log('Calling: ' . $res['handler'] . '.php'); + debug_log('With modifiers: ' . $res['modifiers']); + include_once(ROOT_PATH . '/mods/' . $res['handler'] . '.php'); + + debug_log('Response handeled successfully!'); + // Delete the entry if the call was handled without errors + my_query('DELETE FROM user_input WHERE id = ?', [$res['id']]); + + exit(); } -} + } -$dbh = null; -?> + // Logic to get the command + include_once(CORE_BOT_PATH . '/commands.php'); +} diff --git a/lang/help.json b/lang/help.json index 5a4d61b7..86f59bb4 100644 --- a/lang/help.json +++ b/lang/help.json @@ -1,4 +1,56 @@ { + "help_only_bool": { + "NL": "Alleen true/false of 0/1", + "DE": "nur true/false oder 0/1", + "EN": "only true/false or 0/1", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "true/false ou 0/1 uniquement", + "PL": "TRANSLATE", + "FI": "vain true/false tai 0/1", + "ES": "solo true/false o 0/1" + }, + "help_only_numbers": { + "NL": "Alleen nummers", + "DE": "nur Zahlen", + "EN": "only numbers", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "nombres uniquement", + "PL": "TRANSLATE", + "FI": "vain numeroita", + "ES": "solo números" + }, + "help_bool_expected": { + "NL": "Toegestaande waarde: true/false of 0/1", + "DE": "Zulässige Werte: true/false oder 0/1", + "EN": "Allowed values: true/false or 0/1", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Valeurs autorisées: true/false ou 0/1", + "PL": "TRANSLATE", + "FI": "Sallitut arvot: true/false tai 0/1", + "ES": "Valores validos: true/false o 0/1" + }, + "help_number_expected": { + "NL": "Toegestaande waarde: Nummers groter dan 0", + "DE": "Zulässiger Wert: Zahlen größer 0", + "EN": "Allowed value: Numbers greater than 0", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Valeurs autorisées: nombres supérieurs à 0", + "PL": "TRANSLATE", + "FI": "Sallittu arvo: 0 suuremmat luvut", + "ES": "Valores validos: Números mayores que 0" + }, "help_create_via_location": { "NL": "Locatie delen - Raid aanmaken", "DE": "Standort teilen - Raid anlegen", @@ -7,7 +59,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Partager la position - Créer un raid", "PL": "TRANSLATE", "FI": "Jaa sijainti - Luo raidi", "ES": "Compartir ubicación - Crear incursión" @@ -20,7 +72,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "/start - Créer un raid", "PL": "TRANSLATE", "FI": "/start - Luo raidi", "ES": "/start - Crear incursión" @@ -33,7 +85,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "/start - Créer un raid EX", "PL": "TRANSLATE", "FI": "/start - Luo ex-raidi", "ES": "/start - Crear incursión EX" @@ -46,7 +98,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "/start - Modifier la durée du raid", "PL": "TRANSLATE", "FI": "/start - Vaihda raidin kestoa", "ES": "/start - Cambiar duración de la incursión" @@ -59,7 +111,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "/list - Afficher les 12 derniers raids en cours", "PL": "TRANSLATE", "FI": "/list - Edelliset 12 aktiivista raidia", "ES": "/list - Últimas 12 incursiones activas" @@ -72,7 +124,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "/listall - Afficher tous les raids en cours", "PL": "TRANSLATE", "FI": "/listall - Kaikki aktiiviset raidit", "ES": "/listall - Todas las incursiones activas" @@ -85,7 +137,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "/overview - Gérer l'aperçu de raid", "PL": "TRANSLATE", "FI": "/overview - Hallinnoi raidiyhteenvetoa", "ES": "/overview - Administrar descripción general de la incursión" @@ -98,7 +150,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "/delete - Supprimer vos propres raids", "PL": "TRANSLATE", "FI": "/delete - Poista omia raidejasi", "ES": "/delete - Eliminar incursiones propias" @@ -111,7 +163,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "/delete - Supprimer tous les raids", "PL": "TRANSLATE", "FI": "/delete - Poista kaikki raidit", "ES": "/delete - eliminar incursiones" @@ -124,7 +176,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "/pokemon - Actualiser le Pokémon (propres raids)", "PL": "TRANSLATE", "FI": "/pokemon - Muuta pokemonia (omat raidisi)", "ES": "/pokemon - Actualizar Pokémon (incursiones propias)" @@ -137,7 +189,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "/pokemon - Actualiser le Pokémon (tous les raids)", "PL": "TRANSLATE", "FI": "/pokemon - Muuta pokemonia (kaikki raidit)", "ES": "/pokemon - Actualizar Pokémon (todas incursiones)" @@ -150,7 +202,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "/trainer - Modifier vos informations de dresseur", "PL": "TRANSLATE", "FI": "/trainer - Muuta kouluttajatietojasi", "ES": "/trainer - Editar datos del entrenador" @@ -163,7 +215,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "/gym - Afficher les détails de l'arène", "PL": "TRANSLATE", "FI": "/gym - Näytä salin tiedot", "ES": "/gym - Ver detalles del gimnasio" @@ -176,76 +228,11 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "/gym - Modifier les informations détaillées de l'arène", "PL": "TRANSLATE", "FI": "/gym - Muuta salin lisätietoja", "ES": "/gym - Editar detalles extendidos del gimnasio" }, - "help_gym-name": { - "NL": "/gymname - Verander de gym naam", - "DE": "/gymname - Arena-Namen bearbeiten", - "EN": "/gymname - Edit gym name", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "/gymname - Muuta salin nimeä", - "ES": "/gymname - Editar nombre del gimnasio" - }, - "help_gym-address": { - "NL": "/gymaddress - Verander gym adres", - "DE": "/gymaddress - Arena-Adresse bearbeiten", - "EN": "/gymaddress - Edit gym address", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "/gymaddress - Muuta salin osoitetta", - "ES": "/gymaddress - Editar dirección del gimnasio" - }, - "help_gym-gps": { - "NL": "/gymgps - Verander gym coordinaten", - "DE": "/gymgps - Arena-Koordinaten bearbeiten", - "EN": "/gymgps - Edit gym coordinates", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "/gymgps - Muuta salin koordinaatteja", - "ES": "/gymgps - Editar coordenadas del gimnasio" - }, - "help_gym-note": { - "NL": "/gymnote - Verander extra gym informatie", - "DE": "/gymnote - Arena-Notiz bearbeiten", - "EN": "/gymnote - Edit gym note", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "/gymnote - Muuta salin muistiinpanoja", - "ES": "/gymnote - Editar nota del gimnasio" - }, - "help_gym-add": { - "NL": "/addgym - Gym toevoegen", - "DE": "/addgym - Arena hinzufügen", - "EN": "/addgym - Add a gym", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "/addgym - Lisää sali", - "ES": "/addgym - Añadir gimnasio" - }, "help_portal-import": { "NL": "@Ingressportalbot - Importeer gym", "DE": "@Ingressportalbot - Arena importieren", @@ -254,7 +241,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "@Ingressportalbot - Importer l'arène", "PL": "TRANSLATE", "FI": "@Ingressportalbot - Tuo sali", "ES": "@Ingressportalbot - Importar gimnasio" @@ -267,7 +254,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "/pokedex - Gérer les boss de raid", "PL": "TRANSLATE", "FI": "/pokedex - Hallinnoi raidibosseja", "ES": "/pokedex - Gestionar jefes de incursión" @@ -280,7 +267,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "/get - Afficher la configuration du bot", "PL": "TRANSLATE", "FI": "/get - Näytä botin asetukset", "ES": "/get - Ver configuración del bot" @@ -293,7 +280,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "/set - Modifier la configuration du bot", "PL": "TRANSLATE", "FI": "/set - Muuta botin asetuksia", "ES": "/set - Editar configuración del bot" @@ -306,9 +293,36 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "/help - Afficher l'aide", "PL": "TRANSLATE", "FI": "/help - Näytä ohjeet", "ES": "/help - Ver ayuda" + }, + "help_history": { + "NL": "TRANSLATE", + "DE": "/history - Historie von Raids mit Teilnehmern ansehen", + "EN": "/history - View history of raids that had attendees", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "/history - Afficher l'historique des raids avec participants", + "PL": "TRANSLATE", + "FI": "/history - Listaa päättyneet raidit joissa oli osallistujia", + "ES": "TRANSLATE" + }, + "help_event-manage": { + "NL": "TRANSLATE", + "DE": "/events - Raid-Events verwalten", + "EN": "/events - Manage raid events", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "/events - Gérer les événements de raid", + "PL": "TRANSLATE", + "FI": "/events - Hallitse raiditapahtumia", + "ES": "TRANSLATE" } + } diff --git a/lang/language.json b/lang/language.json index cf1af5fe..ab2fdc75 100644 --- a/lang/language.json +++ b/lang/language.json @@ -1,4 +1,849 @@ { + "lang_name": { + "NL": "Nederlands", + "DE": "Deutsch", + "EN": "English", + "IT": "Italiano", + "PT-BR": "Português", + "RU": "Русский", + "NO": "Norsk", + "FR": "Français", + "PL": "Polski", + "FI": "Suomi", + "ES": "Español" + }, + "weekday_1": { + "NL": "Maandag", + "DE": "Montag", + "EN": "Monday", + "IT": "Lunedì", + "PT-BR": "segunda-feira", + "RU": "Понедельник", + "NO": "Mandag", + "FR": "Lundi", + "PL": "Poniedziałek", + "FI": "Maanantai", + "ES": "Lunes" + }, + "weekday_2": { + "NL": "Dinsdag", + "DE": "Dienstag", + "EN": "Tuesday", + "IT": "Martedì", + "PT-BR": "terça-feira", + "RU": "Вторник", + "NO": "Tirsdag", + "FR": "Mardi", + "PL": "Wtorek", + "FI": "Tiistai", + "ES": "Martes" + }, + "weekday_3": { + "NL": "Woensdag", + "DE": "Mittwoch", + "EN": "Wednesday", + "IT": "Mercoledì", + "PT-BR": "quarta-feira", + "RU": "Среда", + "NO": "Onsdag", + "FR": "Mercredi", + "PL": "Środa", + "FI": "Keskiviikko", + "ES": "Miércoles" + }, + "weekday_4": { + "NL": "Donderdag", + "DE": "Donnerstag", + "EN": "Thursday", + "IT": "Giovedì", + "PT-BR": "quinta-feira", + "RU": "Четверг", + "NO": "Torsdag", + "FR": "Jeudi", + "PL": "Czwartek", + "FI": "Torstai", + "ES": "Jueves" + }, + "weekday_5": { + "NL": "Vrijdag", + "DE": "Freitag", + "EN": "Friday", + "IT": "Venerdì", + "PT-BR": "sexta-feira", + "RU": "Пятница", + "NO": "Fredag", + "FR": "Vendredi", + "PL": "Piątek", + "FI": "Perjantai", + "ES": "Viernes" + }, + "weekday_6": { + "NL": "Zaterdag", + "DE": "Samstag", + "EN": "Saturday", + "IT": "Sabato", + "PT-BR": "sábado", + "RU": "Суббота", + "NO": "Lørdag", + "FR": "Samedi", + "PL": "Sobota", + "FI": "Lauantai", + "ES": "Sábado" + }, + "weekday_7": { + "NL": "Zondag", + "DE": "Sonntag", + "EN": "Sunday", + "IT": "Domenica", + "PT-BR": "domingo", + "RU": "Воскресение", + "NO": "Søndag", + "FR": "Dimanche", + "PL": "Niedziela", + "FI": "Sunnuntai", + "ES": "Domingo" + }, + "month_01": { + "NL": "Januari", + "DE": "Januar", + "EN": "January", + "IT": "Gennaio", + "PT-BR": "Janeiro", + "RU": "Январь", + "NO": "Januar", + "FR": "Janvier", + "PL": "Styczeń", + "FI": "Tammikuu", + "ES": "Enero" + }, + "month_02": { + "NL": "Februari", + "DE": "Februar", + "EN": "February", + "IT": "Febbraio", + "PT-BR": "Fevereiro", + "RU": "Февраль", + "NO": "Februar", + "FR": "Février", + "PL": "Luty", + "FI": "Helmikuu", + "ES": "Febrero" + }, + "month_03": { + "NL": "Maart", + "DE": "März", + "EN": "March", + "IT": "Marzo", + "PT-BR": "Março", + "RU": "Март", + "NO": "Mars", + "FR": "Mars", + "PL": "Marzec", + "FI": "Maaliskuu", + "ES": "Marzo" + }, + "month_04": { + "NL": "April", + "DE": "April", + "EN": "April", + "IT": "Aprile", + "PT-BR": "Abril", + "RU": "Апрель", + "NO": "April", + "FR": "Avril", + "PL": "Kwiecień", + "FI": "Huhtikuu", + "ES": "Abril" + }, + "month_05": { + "NL": "Mei", + "DE": "Mai", + "EN": "May", + "IT": "Maggio", + "PT-BR": "Maio", + "RU": "Май", + "NO": "Mai", + "FR": "Mai", + "PL": "Maj", + "FI": "Toukokuu", + "ES": "Mayo" + }, + "month_06": { + "NL": "Juni", + "DE": "Juni", + "EN": "June", + "IT": "Giugno", + "PT-BR": "Junho", + "RU": "Июнь", + "NO": "Juni", + "FR": "Juin", + "PL": "Czerwiec", + "FI": "Kesäkuu", + "ES": "Junio" + }, + "month_07": { + "NL": "Juli", + "DE": "Juli", + "EN": "July", + "IT": "Luglio", + "PT-BR": "Julho", + "RU": "Июль", + "NO": "Juli", + "FR": "Juillet", + "PL": "Lipiec", + "FI": "Heinäkuu", + "ES": "Julio" + }, + "month_08": { + "NL": "Augustus", + "DE": "August", + "EN": "August", + "IT": "Agosto", + "PT-BR": "Agosto", + "RU": "Август", + "NO": "August", + "FR": "Août", + "PL": "Sierpień", + "FI": "Elokuu", + "ES": "Agosto" + }, + "month_09": { + "NL": "September", + "DE": "September", + "EN": "September", + "IT": "Settembre", + "PT-BR": "Setembro", + "RU": "Сентябрь", + "NO": "September", + "FR": "Septembre", + "PL": "Wrzesień", + "FI": "Syyskuu", + "ES": "Septiembre" + }, + "month_10": { + "NL": "Oktober", + "DE": "Oktober", + "EN": "October", + "IT": "Ottobre", + "PT-BR": "Outubro", + "RU": "Октябрь", + "NO": "Oktober", + "FR": "Octobre", + "PL": "Październik", + "FI": "Lokakuu", + "ES": "Octubre" + }, + "month_11": { + "NL": "November", + "DE": "November", + "EN": "November", + "IT": "Novembre", + "PT-BR": "Novembro", + "RU": "Ноябрь", + "NO": "November", + "FR": "Novembre", + "PL": "Listopad", + "FI": "Marraskuu", + "ES": "Noviembre" + }, + "month_12": { + "NL": "December", + "DE": "Dezember", + "EN": "December", + "IT": "Dicembre", + "PT-BR": "Dezembro", + "RU": "Декабрь", + "NO": "Desember", + "FR": "Décembre", + "PL": "Grudzień", + "FI": "Joulukuu", + "ES": "Diciembre" + }, + "bot_access_denied": { + "NL": "Je hebt geen toegang tot deze functie of bot!", + "DE": "Sie haben keine Berechtigung diesen Befehl oder Bot zu nutzen!", + "EN": "You are not allowed to use this command or bot!", + "IT": "Non hai i permessi necessari per usare questo comando o bot!", + "PT-BR": "Você não tem permissão para utilizar esse comando ou bot!", + "RU": "Вы не можете использовать эту команду или бота!", + "NO": "Du har ikke tillatelse til å bruke denne kommandoen eller boten!", + "FR": "Vous n'êtes pas autorisé à utiliser le bot ou cette commande!", + "PL": "Nie masz uprawnień do korzystania z tej komendy lub bota!", + "FI": "Sinulla ei ole oikeutta tähän komentoon tai bottiin!", + "ES": "¡No está autorizado a utilizar este comando o bot!" + }, + "directions": { + "NL": "Routebeschrijving", + "DE": "Wegbeschreibung", + "EN": "Directions", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Itinéraire", + "PL": "TRANSLATE", + "FI": "Reittiohje", + "ES": "Abrir mapa" + }, + "pokemon": { + "NL": "Pokemon", + "DE": "Pokemon", + "EN": "Pokemon", + "IT": "Pokemon", + "PT-BR": "Pokemon", + "RU": "Pokemon", + "NO": "Pokemon", + "FR": "Pokémon", + "PL": "Pokemon", + "FI": "Pokemon", + "ES": "Pokémon" + }, + "pokemon_types": { + "NL": "Pokemon Types", + "DE": "Pokemon-Typen", + "EN": "Pokemon Types", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Types de Pokémon", + "PL": "TRANSLATE", + "FI": "Pokemon Tyypit", + "ES": "Tipos de Pokémon" + }, + "yes": { + "NL": "Ja", + "DE": "Ja", + "EN": "Yes", + "IT": "Sì", + "PT-BR": "Sim", + "RU": "Да", + "NO": "Ja", + "FR": "Oui", + "PL": "Tak", + "FI": "Kyllä", + "ES": "Si" + }, + "no": { + "NL": "Nee", + "DE": "Nein", + "EN": "No", + "IT": "No", + "PT-BR": "Não", + "RU": "Нет", + "NO": "Nei", + "FR": "Non", + "PL": "Nie", + "FI": "Ei", + "ES": "No" + }, + "or": { + "NL": "of", + "DE": "oder", + "EN": "or", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "ou", + "PL": "TRANSLATE", + "FI": "tai", + "ES": "o" + }, + "not_supported": { + "NL": "Niet ondersteund", + "DE": "Nicht unterstützt", + "EN": "Not supported", + "IT": "Non compatibile", + "PT-BR": "Não compatível", + "RU": "Не поддерживается", + "NO": "Ikke støttet", + "FR": "Non compatible", + "PL": "Nieobsługiwane", + "FI": "Ei tuettu", + "ES": "No soportado" + }, + "abort": { + "NL": "Afbreken", + "DE": "Abbrechen", + "EN": "Abort", + "IT": "Cancella", + "PT-BR": "Abortar", + "RU": "Прервать", + "NO": "Avbryt", + "FR": "Abandonner", + "PL": "Anuluj", + "FI": "Peruuta", + "ES": "Cancelar" + }, + "action_aborted": { + "NL": "Het proces is afgebroken!", + "DE": "Der Vorgang wurde abgebrochen!", + "EN": "The process was aborted!", + "IT": "Il processo è stato cancellato!", + "PT-BR": "O processo foi abortado!", + "RU": "Процесс был прерван!", + "NO": "Handlingen ble avbrutt!", + "FR": "Le processus a été interrompu!", + "PL": "Zadanie zostało anulowane", + "FI": "Prosessi peruutettiin!", + "ES": "¡El proceso fue cancelado!" + }, + "select_action": { + "NL": "Selecteer actie:", + "DE": "Aktion auswählen:", + "EN": "Select action:", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Sélectionnez l'action:", + "PL": "TRANSLATE", + "FI": "Valitse toiminto:", + "ES": "Selecciona la acción:" + }, + "back": { + "NL": "Terug", + "DE": "Zurück", + "EN": "Back", + "IT": "Precedente", + "PT-BR": "Voltar", + "RU": "Назад", + "NO": "Tilbake", + "FR": "Retour", + "PL": "Wstecz", + "FI": "Takaisin", + "ES": "Volver" + }, + "next": { + "NL": "Volgende", + "DE": "Weiter", + "EN": "Next", + "IT": "Successivo", + "PT-BR": "Próximo", + "RU": "Вперед", + "NO": "Neste", + "FR": "Suivant", + "PL": "Dalej", + "FI": "Seuraava", + "ES": "Siguiente" + }, + "done": { + "NL": "Klaar", + "DE": "Fertig", + "EN": "Done", + "IT": "Completato", + "PT-BR": "Feito", + "RU": "Готово", + "NO": "Ferdig", + "FR": "Terminé", + "PL": "Zrobione", + "FI": "Valmis", + "ES": "Hecho" + }, + "add": { + "NL": "Toevoegen", + "DE": "Hinzufügen", + "EN": "Add", + "IT": "Aggiungi", + "PT-BR": "Adicionar", + "RU": "Добавить", + "NO": "Legg til", + "FR": "Ajouter", + "PL": "Dodaj", + "FI": "Lisää", + "ES": "Añadir" + }, + "replace": { + "NL": "Vervang", + "DE": "Ersetzen", + "EN": "Replace", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Remplacer", + "PL": "TRANSLATE", + "FI": "Korvaa", + "ES": "Reemplazar" + }, + "delete": { + "NL": "Verwijder", + "DE": "Löschen", + "EN": "Delete", + "IT": "Elimina", + "PT-BR": "Deletar", + "RU": "Удалить", + "NO": "Slett", + "FR": "Supprimer", + "PL": "Usuń", + "FI": "Poista", + "ES": "Borrar" + }, + "disabled": { + "NL": "TRANSLATE", + "DE": "TRANSLATE", + "EN": "Disabled", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "TRANSLATE", + "PL": "TRANSLATE", + "FI": "Pois käytöstä", + "ES": "TRANSLATE" + }, + "enabled": { + "NL": "TRANSLATE", + "DE": "TRANSLATE", + "EN": "Enabled", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "TRANSLATE", + "PL": "TRANSLATE", + "FI": "Käytössä", + "ES": "TRANSLATE" + }, + "disable": { + "NL": "TRANSLATE", + "DE": "TRANSLATE", + "EN": "Disable", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "TRANSLATE", + "PL": "TRANSLATE", + "FI": "Poista käytöstä", + "ES": "TRANSLATE" + }, + "enable": { + "NL": "TRANSLATE", + "DE": "TRANSLATE", + "EN": "Enable", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "TRANSLATE", + "PL": "TRANSLATE", + "FI": "Ota käyttöön", + "ES": "TRANSLATE" + }, + "edit": { + "NL": "Bewerken", + "DE": "Bearbeiten", + "EN": "Edit", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Modifier", + "PL": "TRANSLATE", + "FI": "Muokkaa", + "ES": "Editar" + }, + "list": { + "NL": "Lijst", + "DE": "Anzeigen", + "EN": "List", + "IT": "Lista", + "PT-BR": "Lista", + "RU": "Список", + "NO": "List", + "FR": "Liste", + "PL": "Pokaż", + "FI": "Listaa", + "ES": "Lista" + }, + "reset": { + "NL": "Reset", + "DE": "Reset", + "EN": "Reset", + "IT": "Reset", + "PT-BR": "Resetar", + "RU": "Сброс", + "NO": "Reset", + "FR": "Réinitialiser", + "PL": "Resetuj", + "FI": "Resetoi", + "ES": "Reiniciar" + }, + "created_by": { + "NL": "Aangemaakt door", + "DE": "Erstellt von", + "EN": "Created by", + "IT": "Creato da", + "PT-BR": "Criada por", + "RU": "Создано", + "NO": "Postet av", + "FR": "Créé par", + "PL": "Stworzone przez", + "FI": "Luonut", + "ES": "Creado por" + }, + "updated": { + "NL": "Bijgewerkt", + "DE": "Aktualisiert", + "EN": "Updated", + "IT": "Aggiornato", + "PT-BR": "Atualizada", + "RU": "Обновлено", + "NO": "Oppdatert", + "FR": "Mis à jour", + "PL": "Zaktualizowano", + "FI": "Päivitetty", + "ES": "Actualizado" + }, + "share": { + "NL": "Delen", + "DE": "Teilen", + "EN": "Share", + "IT": "Condividi", + "PT-BR": "Compartilhar", + "RU": "Поделиться", + "NO": "Del", + "FR": "Partager", + "PL": "Udostępnij", + "FI": "Jaa", + "ES": "Compartir" + }, + "share_with": { + "NL": "Delen met", + "DE": "Teilen mit", + "EN": "Share with", + "IT": "Condividi con", + "PT-BR": "Compartilhar com", + "RU": "Поделиться с", + "NO": "Del med", + "FR": "Partager avec", + "PL": "Przekaż dalej", + "FI": "Jaa ryhmään", + "ES": "Compartir con" + }, + "successfully_shared": { + "NL": "Succesvol gedeeld!", + "DE": "Erfolgreich geteilt!", + "EN": "Successfully shared!", + "IT": "Condiviso con successo!", + "PT-BR": "Compartilhado com sucesso!", + "RU": "Успешно отправлено!", + "NO": "Deling velykket!", + "FR": "Partagé avec succès!", + "PL": "Pomyślnie udostępniono", + "FI": "Jaettu onnistuneesti!", + "ES": "¡Compartido con éxito!" + }, + "config": { + "NL": "Configuratie", + "DE": "Konfiguration", + "EN": "Configuration", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Configuration", + "PL": "TRANSLATE", + "FI": "Asetukset", + "ES": "Configuración" + }, + "config_updated": { + "NL": "Configuratie ge-update", + "DE": "Konfiguration aktualisiert", + "EN": "Configuration updated", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Configuration mise à jour", + "PL": "TRANSLATE", + "FI": "Asetukset päivitetty", + "ES": "Configuración actualizada" + }, + "resetted": { + "NL": "Gereset", + "DE": "Zurückgesetzt", + "EN": "Reset", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Réinitialisé", + "PL": "TRANSLATE", + "FI": "Resetoitu", + "ES": "Reiniciado" + }, + "option_value": { + "NL": "Configuratie optie waarde", + "DE": "Konfigurationsoption Wert", + "EN": "Configuration option value", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Valeur de l'option de configuration", + "PL": "TRANSLATE", + "FI": "Asetuksen arvo", + "ES": "Valor de la opción de configuración" + }, + "config_option": { + "NL": "Configuratie optie", + "DE": "Konfigurationsoption", + "EN": "Configuration option", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Option de configuration", + "PL": "TRANSLATE", + "FI": "Asetuksen nimi", + "ES": "TRANSLATE" + }, + "no_value": { + "NL": "Geen waarde", + "DE": "Kein Wert", + "EN": "No value", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Aucune valeur", + "PL": "TRANSLATE", + "FI": "Ei arvoa", + "ES": "Sin valor" + }, + "new_value": { + "NL": "Nieuwe waarde:", + "DE": "Neuer Wert:", + "EN": "New value:", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Nouvelle valeur:", + "PL": "TRANSLATE", + "FI": "Uusi arvo:", + "ES": "Nuevo valor:" + }, + "old_value": { + "NL": "Oude waarde:", + "DE": "Alter Wert:", + "EN": "Old value:", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Ancienne valeur:", + "PL": "TRANSLATE", + "FI": "Vanha arvo:", + "ES": "Antiguo valor:" + }, + "personal_help": { + "NL": "Alle commando's voor de bot:", + "DE": "Deine persönliche Bot-Hilfe:", + "EN": "Your personal bot help:", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Aide personnalisée du bot:", + "PL": "TRANSLATE", + "FI": "Henkilökohtainen bottiapu:", + "ES": "Tu bot personal ayuda:" + }, + "invalid_input": { + "NL": "Ongeldige input!", + "DE": "Ungültige Eingabe!", + "EN": "Invalid input!", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Entrée invalide!", + "PL": "TRANSLATE", + "FI": "Epäkelpo arvo!", + "ES": "¡Entrada inválida!" + }, + "internal_error": { + "NL": "Internal error!", + "DE": "Interner Fehler!", + "EN": "Internal error!", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Erreur interne!", + "PL": "TRANSLATE", + "FI": "Sisäinen virhe!", + "ES": "¡Error interno!" + }, + "expand": { + "NL": "Uitklappen", + "DE": "Ausklappen", + "EN": "Expand", + "IT": "Espandi", + "PT-BR": "Expandir", + "RU": "Развернуть", + "NO": "Flere valg", + "FR": "Agrandir", + "PL": "Rozwiń", + "FI": "Laajenna", + "ES": "Expandir" + }, + "none": { + "NL": "Geen", + "DE": "Keine", + "EN": "None", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Aucun", + "PL": "TRANSLATE", + "FI": "ei mitään", + "ES": "Nada" + }, + "other": { + "NL": "Anders", + "DE": "Sonstiges", + "EN": "Other", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Autres", + "PL": "TRANSLATE", + "FI": "Muu", + "ES": "Otro" + }, + "tutorial_vote_failed": { + "NL": "Je kan niet meedoen voordat je de tutorial hebt doorlopen.", + "DE": "Du kannst nicht abstimmen, bevor du das Tutorial durchlaufen hast.", + "EN": "You can't vote before going through the tutorial.", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Vous ne pouvez pas voter avant d'avoir suivi le tutoriel.", + "PL": "TRANSLATE", + "FI": "Et voi äänestää ennen kuin olet käynyt ohjeen läpi.", + "ES": "No puedes votar antes de seguir el tutorial." + }, + "tutorial_command_failed": { + "NL": "Je kan dit commando niet gebruiken voordat je de tutorial hebt doorlopen.", + "DE": "Du kannst diesen Befehl nicht verwenden, bevor du das Tutorial durchlaufen hast.", + "EN": "You can't use this command before going through the tutorial.", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Vous ne pouvez pas utiliser cette commande avant d'avoir suivi le tutoriel.", + "PL": "TRANSLATE", + "FI": "Et voi käyttää tätä käskyä ennen kuin olet käynyt ohjeen läpi.", + "ES": "No puede usar este comando antes de seguir el tutorial." + }, "raid": { "NL": "Raid", "DE": "Raid", @@ -12,7 +857,7 @@ "FI": "Raidi", "ES": "Incursión" }, - "Xstars": { + "99stars": { "NL": "Ex-Raid", "DE": "Ex-Raid", "EN": "Ex-Raid", @@ -20,11 +865,257 @@ "PT-BR": "Ex-Raid", "RU": "Экс-Рейд", "NO": "Ex-Raid", - "FR": "Raid Ex", + "FR": "Raid EX", "PL": "Ex-Rajd", "FI": "Ex-Raidi", "ES": "Incursión EX" }, + "17stars": { + "EN": "5 Super Mega Raid" + }, + "17stars_short": { + "EN": "5 Super Mega" + }, + "16stars": { + "EN": "4 Super Mega Raid" + }, + "16stars_short": { + "EN": "4 Super Mega" + }, + "15stars": { + "NL": "TRANSLATE", + "DE": "5 Sterne Shadow", + "EN": "5 star shadow", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Obscur niveau 5", + "PL": "TRANSLATE", + "FI": "5 tähden shadow", + "ES": "TRANSLATE" + }, + "15stars_short": { + "NL": "TRANSLATE", + "DE": "Shadow 5", + "EN": "Shadow 5", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Obscur 5", + "PL": "TRANSLATE", + "FI": "Shadow 5", + "ES": "TRANSLATE" + }, + "14stars": { + "NL": "TRANSLATE", + "DE": "4 Sterne Shadow", + "EN": "4 star shadow", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Obscur niveau 4", + "PL": "TRANSLATE", + "FI": "4 tähden shadow", + "ES": "TRANSLATE" + }, + "14stars_short": { + "NL": "TRANSLATE", + "DE": "Shadow 4", + "EN": "Shadow 4", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Obscur 4", + "PL": "TRANSLATE", + "FI": "Shadow 4", + "ES": "TRANSLATE" + }, + "13stars": { + "NL": "TRANSLATE", + "DE": "3 Sterne Shadow", + "EN": "3 star shadow", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Obscur niveau 3", + "PL": "TRANSLATE", + "FI": "3 tähden shadow", + "ES": "TRANSLATE" + }, + "13stars_short": { + "NL": "TRANSLATE", + "DE": "Shadow 3", + "EN": "Shadow 3", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Obscur 3", + "PL": "TRANSLATE", + "FI": "Shadow 3", + "ES": "TRANSLATE" + }, + "12stars": { + "NL": "TRANSLATE", + "DE": "2 Sterne Shadow", + "EN": "2 star shadow", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Obscur niveau 2", + "PL": "TRANSLATE", + "FI": "2 tähden shadow", + "ES": "TRANSLATE" + }, + "12stars_short": { + "NL": "TRANSLATE", + "DE": "Shadow 2", + "EN": "Shadow 2", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Obscur 2", + "PL": "TRANSLATE", + "FI": "Shadow 2", + "ES": "TRANSLATE" + }, + "11stars": { + "NL": "TRANSLATE", + "DE": "1 Stern Shadow", + "EN": "1 star shadow", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Obscur niveau 1", + "PL": "TRANSLATE", + "FI": "1 tähden shadow", + "ES": "TRANSLATE" + }, + "11stars_short": { + "NL": "TRANSLATE", + "DE": "Shadow 1", + "EN": "Shadow 1", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Obscur 1", + "PL": "TRANSLATE", + "FI": "Shadow 1", + "ES": "TRANSLATE" + }, + "10stars": { + "NL": "Primal raid", + "DE": "Primal-Raid", + "EN": "Primal raid", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Primo-raid", + "PL": "TRANSLATE", + "FI": "Primal raid", + "ES": "TRANSLATE" + }, + "10stars_short": { + "NL": "Primal", + "DE": "Primal", + "EN": "Primal", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Primo", + "PL": "TRANSLATE", + "FI": "Primal", + "ES": "TRANSLATE" + }, + "9stars": { + "NL": "Elite raid", + "DE": "Elite-Raid", + "EN": "Elite raid", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Raid d'élite", + "PL": "TRANSLATE", + "FI": "Elite raid", + "ES": "TRANSLATE" + }, + "9stars_short": { + "NL": "Elite", + "DE": "Elite", + "EN": "Elite", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Élite", + "PL": "TRANSLATE", + "FI": "Elite", + "ES": "TRANSLATE" + }, + "8stars": { + "NL": "Ultra Wormhole", + "DE": "Ultrapforte", + "EN": "Ultra Wormhole", + "IT": "Ultravarco", + "PT-BR": "Ultrafenda Espacial", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Raid Ultra-Chimère", + "PL": "Ultratunel", + "FI": "Ultramadonreikä", + "ES": "Ultraumbral" + }, + "8stars_short": { + "NL": "Ultra", + "DE": "Ultra", + "EN": "Ultra", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Ultra", + "PL": "TRANSLATE", + "FI": "TRANSLATE", + "ES": "TRANSLATE" + }, + "7stars": { + "NL": "Legendary Mega-Raid", + "DE": "Legendäres Mega-Raid", + "EN": "Legendary Mega-Raid", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Méga-raid légendaire", + "PL": "TRANSLATE", + "FI": "Legendaarinen Mega-Raidi", + "ES": "TRANSLATE" + }, + "7stars_short": { + "NL": "Lege Mega", + "DE": "Legend Mega", + "EN": "Lege Mega", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Méga lég", + "PL": "TRANSLATE", + "FI": "Lege Mega", + "ES": "TRANSLATE" + }, "6stars": { "NL": "Mega-Raid", "DE": "Mega-Raid", @@ -33,11 +1124,24 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Méga-raid", "PL": "TRANSLATE", "FI": "Mega-Raidi", "ES": "Mega Incursión" }, + "6stars_short": { + "NL": "Mega", + "DE": "Mega", + "EN": "Mega", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Méga", + "PL": "TRANSLATE", + "FI": "Mega", + "ES": "Mega" + }, "5stars": { "NL": "5 Sterren", "DE": "5 Sterne", @@ -64,57 +1168,200 @@ "FI": "4 tähden", "ES": "Nivel 4" }, - "3stars": { - "NL": "3 Sterren", - "DE": "3 Sterne", - "EN": "3 Star", - "IT": "3 Stelle", - "PT-BR": "Nível 3", - "RU": "3 Звезды", - "NO": "Level 3", - "FR": "Niveau 3", - "PL": "3 poziom", - "FI": "3 tähden", - "ES": "Nivel 3" + "3stars": { + "NL": "3 Sterren", + "DE": "3 Sterne", + "EN": "3 Star", + "IT": "3 Stelle", + "PT-BR": "Nível 3", + "RU": "3 Звезды", + "NO": "Level 3", + "FR": "Niveau 3", + "PL": "3 poziom", + "FI": "3 tähden", + "ES": "Nivel 3" + }, + "2stars": { + "NL": "2 Sterren", + "DE": "2 Sterne", + "EN": "2 Star", + "IT": "2 Stelle", + "PT-BR": "Nível 2", + "RU": "2 Звезды", + "NO": "Level 2", + "FR": "Niveau 2", + "PL": "2 poziom", + "FI": "2 tähden", + "ES": "Nivel 2" + }, + "1stars": { + "NL": "1 Sterren", + "DE": "1 Sterne", + "EN": "1 Star", + "IT": "1 Stella", + "PT-BR": "Nível 1", + "RU": "1 Звезда", + "NO": "Level 1", + "FR": "Niveau 1", + "PL": "1 poziom", + "FI": "1 tähden", + "ES": "Nivel 1" + }, + "0stars": { + "NL": "Uitgeschakeld", + "DE": "Deaktiviert", + "EN": "Disabled", + "IT": "Disabilitato", + "PT-BR": "Desabilitado", + "RU": "Деактивировано", + "NO": "Deaktivert", + "FR": "Désactivé", + "PL": "Nieaktywny", + "FI": "Pois käytöstä", + "ES": "Desactivado" + }, + "egg_17": { + "NL": "TRANSLATE", + "DE": "TRANSLATE", + "EN": "5 Mega Enhanced Egg", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "TRANSLATE", + "PL": "TRANSLATE", + "FI": "TRANSLATE", + "ES": "TRANSLATE" + }, + "egg_16": { + "NL": "TRANSLATE", + "DE": "TRANSLATE", + "EN": "4 Mega Enhanced Egg", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "TRANSLATE", + "PL": "TRANSLATE", + "FI": "TRANSLATE", + "ES": "TRANSLATE" + }, + "egg_15": { + "NL": "TRANSLATE", + "DE": "Level 5 Shadow Ei", + "EN": "Level 5 shadow egg", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Oeuf obscur niveau 5", + "PL": "TRANSLATE", + "FI": "Tason 5 shadow muna", + "ES": "TRANSLATE" + }, + "egg_14": { + "NL": "TRANSLATE", + "DE": "Level 4 Shadow Ei", + "EN": "Level 4 shadow egg", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Oeuf obscur niveau 4", + "PL": "TRANSLATE", + "FI": "Tason 4 shadow muna", + "ES": "TRANSLATE" + }, + "egg_13": { + "NL": "TRANSLATE", + "DE": "Level 3 Shadow Ei", + "EN": "Level 3 shadow egg", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Oeuf obscur niveau 3", + "PL": "TRANSLATE", + "FI": "Tason 3 shadow muna", + "ES": "TRANSLATE" + }, + "egg_12": { + "NL": "TRANSLATE", + "DE": "Level 2 Shadow Ei", + "EN": "Level 2 shadow egg", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Oeuf obscur niveau 2", + "PL": "TRANSLATE", + "FI": "Tason 2 shadow muna", + "ES": "TRANSLATE" + }, + "egg_11": { + "NL": "TRANSLATE", + "DE": "Level 1 Shadow Ei", + "EN": "Level 1 shadow egg", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Oeuf obscur niveau 1", + "PL": "TRANSLATE", + "FI": "Tason 1 shadow muna", + "ES": "TRANSLATE" + }, + "egg_10": { + "NL": "Primal raid ei", + "DE": "Primal Raid Ei", + "EN": "Primal raid egg", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Oeuf Primo-raid", + "PL": "TRANSLATE", + "FI": "Primal raid -muna", + "ES": "TRANSLATE" }, - "2stars": { - "NL": "2 Sterren", - "DE": "2 Sterne", - "EN": "2 Star", - "IT": "2 Stelle", - "PT-BR": "Nível 2", - "RU": "2 Звезды", - "NO": "Level 2", - "FR": "Niveau 2", - "PL": "2 poziom", - "FI": "2 tähden", - "ES": "Nivel 2" + "egg_9": { + "NL": "Elite raid egg", + "DE": "Elite Raid Ei", + "EN": "Elite raid egg", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Oeuf raid d'élite", + "PL": "TRANSLATE", + "FI": "Elite raid -muna", + "ES": "TRANSLATE" }, - "1stars": { - "NL": "1 Sterren", - "DE": "1 Sterne", - "EN": "1 Star", - "IT": "1 Stella", - "PT-BR": "Nível 1", - "RU": "1 Звезда", - "NO": "Level 1", - "FR": "Niveau 1", - "PL": "1 poziom", - "FI": "1 tähden", - "ES": "Nivel 1" + "egg_8": { + "NL": "Ultra Wormhole", + "DE": "Ultrapforte", + "EN": "Ultra Wormhole", + "IT": "Ultravarco", + "PT-BR": "Ultrafenda Espacial", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Ultra-Brèche", + "PL": "Ultratunel", + "FI": "Ultramadonreikä", + "ES": "Ultraumbral" }, - "0stars": { - "NL": "Uitgeschakeld", - "DE": "Deaktiviert", - "EN": "Disabled", - "IT": "Disabilitato", - "PT-BR": "Desabilitado", - "RU": "Деактивировано", - "NO": "Deaktivert", - "FR": "Désactivé", - "PL": "Nieaktywny", - "FI": "Pois käytöstä", - "ES": "Desactivado" + "egg_7": { + "NL": "Legendary Mega-Raid Ei", + "DE": "Legendäres Mega-Raid Ei", + "EN": "Legendary Mega-Raid Egg", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Oeuf Méga-raid légendaire", + "PL": "TRANSLATE", + "FI": "Legendaarinen Mega-Raid Muna", + "ES": "TRANSLATE" }, "egg_6": { "NL": "Mega-Raid Ei", @@ -124,7 +1371,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Oeuf Méga-raid", "PL": "TRANSLATE", "FI": "Mega-Raid Muna", "ES": "Huevo Mega Incursión" @@ -137,7 +1384,7 @@ "PT-BR": "Ovo de Nível 5", "RU": "Яйцо уровня 5", "NO": "Level 5 Egg", - "FR": "Oeuf 5", + "FR": "Oeuf niveau 5", "PL": "Jajko 5 poziom", "FI": "Tason 5 muna", "ES": "Huevo nivel 5" @@ -150,7 +1397,7 @@ "PT-BR": "Ovo de Nível 4", "RU": "Яйцо уровня 4", "NO": "Level 4 Egg", - "FR": "Oeuf 4", + "FR": "Oeuf niveau 4", "PL": "Jajko 4 poziom", "FI": "Tason 4 muna", "ES": "Huevo nivel 4" @@ -163,7 +1410,7 @@ "PT-BR": "Ovo de Nível 3", "RU": "Яйцо уровня 3", "NO": "Level 3 Egg", - "FR": "Oeuf 3", + "FR": "Oeuf niveau 3", "PL": "Jajko 3 poziom", "FI": "Tason 3 muna", "ES": "Huevo nivel 3" @@ -176,7 +1423,7 @@ "PT-BR": "Ovo de Nível 2", "RU": "Яйцо уровня 2", "NO": "Level 2 Egg", - "FR": "Oeuf 2", + "FR": "Oeuf niveau 2", "PL": "Jajko 2 poziom", "FI": "Tason 2 muna", "ES": "Huevo nivel 2" @@ -189,7 +1436,7 @@ "PT-BR": "Ovo de Nível 1", "RU": "Яйцо уровня 1", "NO": "Level 1 Egg", - "FR": "Oeuf 1", + "FR": "Oeuf niveau 1", "PL": "Jajko 1 poziom", "FI": "Tason 1 muna", "ES": "Huevo nivel 1" @@ -202,98 +1449,111 @@ "PT-BR": "Reide Ovo", "RU": "Рейдовое Яйцо", "NO": "Level 0 Egg", - "FR": "Oeuf 0", + "FR": "Oeuf raid", "PL": "Jajo", "FI": "Raid-muna", "ES": "Huevo Incursión" }, "upcoming": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", + "NL": "geplande", + "DE": "Bevorstehend", "EN": "upcoming", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "à venir", "PL": "TRANSLATE", "FI": "tulevat", "ES": "próximo" }, "upcoming_bosses_not_found": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", + "NL": "Geen informatie gevonden voor geplande raid bazen!", + "DE": "Keine Info zu bevorstehenden Raid-Bossen gefunden!", "EN": "No info of upcoming bosses was found!", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Aucune information sur les prochains boss n'a été trouvée!", "PL": "TRANSLATE", "FI": "Tietoa tulevista bosseista ei löytynyt!", "ES": "¡No se encontró información de los próximos jefes!" }, "current_scheduled_bosses": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", + "NL": "Geplande raid bazen van de database", + "DE": "Geplante Raid-Bosse von der Datenbank", "EN": "Scheduled raid bosses from database", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Boss de raid planifiés dans la base de données", "PL": "TRANSLATE", "FI": "Tallennetut tulevat raidibossit", "ES": "Jefes de incursión programados de la base de datos" }, "unscheduled_bosses": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", + "NL": "Ongeplande bazen", + "DE": "Ungeplante Raid-Bosse", "EN": "Unscheduled bosses", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Boss de raid non planifiés", "PL": "TRANSLATE", "FI": "Ajastamattomat bossit", "ES": "Jefes no programados" }, "delete_scheduled_confirmation": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", + "NL": "Wil je de geplande baas verwijderen?", + "DE": "Diesen geplanten Eintrag löschen?", "EN": "Do you want to delete this scheduled entry?", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Voulez-vous supprimer cette entrée planifiée?", "PL": "TRANSLATE", "FI": "Haluatko poistaa tämän ajastuksen?", "ES": "¿Quieres eliminar esta entrada programada?" }, - "found_upcoming_bosses": { + "edit_scheduled_entry": { "NL": "TRANSLATE", "DE": "TRANSLATE", - "EN": "Found following upcoming bosses", + "EN": "Edit scheduled entry", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", "FR": "TRANSLATE", "PL": "TRANSLATE", + "FI": "Muokkaa ajastusta", + "ES": "TRANSLATE" + }, + "found_upcoming_bosses": { + "NL": "Volgende geplande raid bazen gevonden", + "DE": "Folgende bevorstehende Raid-Bosse gefunden", + "EN": "Found following upcoming bosses", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Prochains boss de raid planifiés trouvés", + "PL": "TRANSLATE", "FI": "Löydetyt tulevat raidibossit", "ES": "Próximos jefes encontrado" }, "confirm_replace_upcoming": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", + "NL": "Wil je de data vervangen met wat we gevonden hebben?", + "DE": "Aktuell vorhandene Daten durch die gefundenen Daten ersetzen?", "EN": "Would you like to replace the current data with what we found?", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Voulez-vous remplacer les données actuelles par celles que nous avons trouvées?", "PL": "TRANSLATE", "FI": "Haluatko korvata nykyiset tiedot nyt löydetyillä?", "ES": "¿Quieres reemplazar los datos actuales con los encontrados?" @@ -306,7 +1566,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Chromatique", "PL": "TRANSLATE", "FI": "Shiny", "ES": "Shiny" @@ -319,7 +1579,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Pas chromatique", "PL": "TRANSLATE", "FI": "Ei shiny", "ES": "No shiny" @@ -371,7 +1631,7 @@ "PT-BR": "Cancelado", "RU": "Отмена", "NO": "Avlysning", - "FR": "Finalement je ne viens pas", + "FR": "Annulation", "PL": "Nie mogę przyjsć", "FI": "Peru", "ES": "Cancelar" @@ -384,7 +1644,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Annulé", "PL": "TRANSLATE", "FI": "Perunut", "ES": "Cancelado" @@ -410,7 +1670,7 @@ "PT-BR": "Votos atualizados", "RU": "Голосование обновлено", "NO": "Stemme oppdatert", - "FR": "Mise à jour du vote", + "FR": "Vote mis à jour", "PL": "Głos zaktualizowany", "FI": "Äänestys päivitetty", "ES": "Voto actualizado" @@ -423,7 +1683,7 @@ "PT-BR": "Vote por um horário primeiro!", "RU": "Сначала выберите время!", "NO": "Foreslå tid først", - "FR": "Votez pour une heure en premier !", + "FR": "Votez d'abord pour une heure!", "PL": "Wybierz najpierw godzinę", "FI": "Äänestä ensin aikaa!", "ES": "¡Primero selecciona un horario!" @@ -436,7 +1696,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Votez pour une heure dans le futur!", "PL": "TRANSLATE", "FI": "Äänestä tulevaa aikaa!", "ES": "¡Vota por un tiempo futuro!" @@ -449,7 +1709,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "La participation à distance n'est plus possible!", "PL": "TRANSLATE", "FI": "Osallistuminen etänä ei ole enää mahdollista!", "ES": "¡Asistir a distancia ya no es posible!" @@ -462,7 +1722,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Vous ne pouvez pas faire ce choix.", "PL": "TRANSLATE", "FI": "Et voi tehdä tätä valintaa.", "ES": "No puedes realizar esta selección." @@ -475,7 +1735,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Ceci est une information de raid. Le vote n'est pas disponible!", "PL": "TRANSLATE", "FI": "Tämä on raid-tiedote, ei äänestystä.", "ES": "Esta es una información de incursión. ¡No es posible votar!" @@ -501,7 +1761,7 @@ "PT-BR": "Ovo eclode", "RU": "Откроется", "NO": "Egget klekker", - "FR": "POP", + "FR": "Éclosion", "PL": "Wyklucie", "FI": "Avautuu", "ES": "Abre" @@ -514,7 +1774,7 @@ "PT-BR": "Ovo eclode em", "RU": "Откроется", "NO": "Klekker", - "FR": "RDV", + "FR": "Éclot le", "PL": "Wykluje się", "FI": "Avautuu", "ES": "Apre en" @@ -540,7 +1800,7 @@ "PT-BR": "Reide até", "RU": "Рейд до", "NO": "Raid fram til", - "FR": "DEPOP", + "FR": "Raid jusqu'à", "PL": "Rajd trwa do", "FI": "Raidi kunnes", "ES": "Incursión hasta" @@ -553,11 +1813,24 @@ "PT-BR": "PARTICIPAÇÃO APENAS COM PASSE EX", "RU": "УЧАСТИЕ ВОЗМОЖНО ТОЛЬКО С ПРОПУСКОМ НА ЭКС-РЕЙД", "NO": "DET ER BARE MULIG Å DELTA OM DU HAR ETT EX-RAID PASS FOR DENNE GYMMEN", - "FR": "PARTICIPATION POSSIBLE SEULEMENT SI VOUS AVEZ UN PASS RAID EX", + "FR": "PARTICIPATION POSSIBLE UNIQUEMENT AVEC UN PASS RAID EX", "PL": "Rajdowanie możliwe tylko z zaproszeniem na EX-Rajd", "FI": "VOIT OSALLISTUA VAIN EX-RAIDI PASSILLA", "ES": "PARTICIPACIÓN SOLO ES POSIBLE CON PASE EX" }, + "no_remote_parcipants": { + "NL": "Deze raid kan alleen lokaal gespeeld worden!", + "DE": "Teilnahme am Raid ist nur vor Ort möglich!", + "EN": "This raid can not be participated using a Remote Raid Pass!", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Il n'est pas possible de participer à ce raid avec un passe à distance!", + "PL": "TRANSLATE", + "FI": "Tähän raidiin ei voi osallistua etäpassilla!", + "ES": "TRANSLATE" + }, "no_participants_yet": { "NL": "Nog geen deelnemers", "DE": "Noch keine Teilnehmer.", @@ -566,7 +1839,7 @@ "PT-BR": "Ainda sem participantes", "RU": "Пока нет участников", "NO": "Ingen deltaker ennå", - "FR": "Aucun participant", + "FR": "Aucun participant actuellement", "PL": "Brak chętnych", "FI": "Ei osallistujia", "ES": "No hay participantes" @@ -579,20 +1852,20 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Importer", "PL": "TRANSLATE", "FI": "Tuo", "ES": "Importar" }, "update_pokemon_table": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", + "NL": "Update Pokemon tabel", + "DE": "Pokemon-Tabelle aktualisieren", "EN": "Update Pokemon table", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Mettre à jour le tableau des Pokémon", "PL": "TRANSLATE", "FI": "Päivitä Pokemon-taulu", "ES": "Actualizar tabla Pokémon" @@ -605,7 +1878,7 @@ "PT-BR": "Finalizada", "RU": "Закончено", "NO": "Ferdig", - "FR": "terminé", + "FR": "Terminé", "PL": "Skończony", "FI": "Valmis", "ES": "Finalizado" @@ -644,7 +1917,7 @@ "PT-BR": "até", "RU": "к", "NO": "til", - "FR": "DEPOP", + "FR": "à", "PL": "do", "FI": "-", "ES": "a" @@ -670,7 +1943,7 @@ "PT-BR": "Alguns participantes estão atrasados.", "RU": "Некоторые участники задерживаются.", "NO": "Noen deltakere er forsinket", - "FR": "Certains participants sont en retard", + "FR": "Certains participants sont en retard.", "PL": "Ktoś się spóźni", "FI": "Osa osallistujista on myöhässä.", "ES": "Algunos participantes llegan tarde." @@ -683,7 +1956,7 @@ "PT-BR": "Por favor espere RAID_LATE_TIME minutos!", "RU": "Пожалуйста подождите RAID_LATE_TIME минут!", "NO": "Vær grei og vent RAID_LATE_TIME minutter!", - "FR": "Attendez s'il vous plaît RAID_LATE_TIME minutes !", + "FR": "Attendez RAID_LATE_TIME minutes s'il vous plaît!", "PL": "Poczekaj RAID_LATE_TIME minut!", "FI": "Odottakaa RAID_LATE_TIME minuuttia!", "ES": "¡Por favor, esperar RAID_LATE_TIME minutos!" @@ -696,7 +1969,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "START_CODE le raid", "PL": "TRANSLATE", "FI": "START_CODE raidi", "ES": "START_CODE incursión" @@ -709,7 +1982,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Informer tous les participants du commencement du raid", "PL": "TRANSLATE", "FI": "Ilmoita kaikille osallistujille raidin alkamisesta", "ES": "Notificar a todos los participantes del inicio de la incursión" @@ -722,7 +1995,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "(notifier les participants)", "PL": "TRANSLATE", "FI": "(viesti kaikille)", "ES": "(Mensaje a todos)" @@ -735,7 +2008,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Groupe privé", "PL": "TRANSLATE", "FI": "Privaattiryhmä", "ES": "Grupo privado" @@ -748,33 +2021,33 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Publique", "PL": "TRANSLATE", "FI": "Julkinen", "ES": "Público" }, "remote_raid": { "NL": "Remote Raid", - "DE": "TRANSLATE", + "DE": "Fern-Raid", "EN": "Remote raid", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Raid à distance", "PL": "TRANSLATE", "FI": "Etäraidi", "ES": "Incursión remota" }, "remote_raids": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", + "NL": "Remote raids", + "DE": "Fern-Raids", "EN": "Remote raids", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Raids à distance", "PL": "TRANSLATE", "FI": "Etäraidit", "ES": "Incursiones remotas" @@ -787,7 +2060,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Participants à distance!", "PL": "TRANSLATE", "FI": "Etäosallistujia!", "ES": "¡Participantes remotos!" @@ -800,46 +2073,46 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "(max. REMOTE_MAX_USERS à la fois)", "PL": "TRANSLATE", "FI": "(max. REMOTE_MAX_USERS kerrallaan)", "ES": "(máx. REMOTE_MAX_USERS cada vez)" }, "delete_remote_raid_done": { "NL": "Je hebt de raid gespeeld die je hebt aangemaakt. Kan deze verwijderd worden?", - "DE": "TRANSLATE", + "DE": "Du hast deinen erstellten Fern-Raid fertig gespielt. Raid-Abstimmung löschen?", "EN": "You have played the remote raid you created. Can we delete the raid poll?", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Vous avez participé au raid à distance que vous avez créé. Voulez-vous supprimer l'annonce du raid?", "PL": "TRANSLATE", "FI": "Olet pelannut luomasi etäraidin. Saako raidi-ilmoituksen poistaa?", "ES": "Has participado en la incursión remota que creaste. ¿Quieres eliminar el anuncio de la incursión?" }, "delete_remote_raid_cancel": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", + "NL": "Je hebt je aanwezigheid bij de remote raid afgezegd. Kunnen we het raid overzicht verwijderen?", + "DE": "Du hast deine Teilnahme an deinem erstellten Fern-Raid abgesagt. Raid-Abstimmung löschen?", "EN": "You have canceled your attendance to the remote raid you created. Can we delete the raid poll?", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Vous avez annulé votre participation au raid à distance que vous avez créé. Voulez-vous supprimer l'annonce du raid?", "PL": "TRANSLATE", "FI": "Olet perunut osallistumisesi luomaasi etäraidin. Saako raidi-ilmoituksen poistaa?", "ES": "Has cancelado tu participación a la incursión remota que creaste. ¿Quieres eliminar el anuncio de la incursión?" }, "remote_raid_marked_ended": { "NL": "Bedankt! Remote raid is geëindigd", - "DE": "TRANSLATE", + "DE": "Danke! Fern-Raid als Beendet markiert!", "EN": "Thank you! Remote raid marked as ended!", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Merci! Raid à distance indiqué comme terminé!", "PL": "TRANSLATE", "FI": "Kiitos! Etäraidi merkattu päättyneeksi!", "ES": "¡Gracias! ¡Incursión remota marcada como finalizada!" @@ -852,7 +2125,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Code de groupe", "PL": "TRANSLATE", "FI": "Ryhmäkoodi", "ES": "Código de grupo" @@ -865,7 +2138,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "(aucun code de groupe requis)", "PL": "TRANSLATE", "FI": "(ei ryhmäkoodia)", "ES": "(no se necesita código de grupo)" @@ -878,7 +2151,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Vous n'êtes PAS inscrit en tant que participant à distance. Demandez le code de groupe sur place!", "PL": "TRANSLATE", "FI": "Et ole rekisteröity etäosallistujaksi. Pyydä koodi paikanpäällä!", "ES": "NO estás registrado como participante remoto. ¡Solicita el código de grupo en el sitio!" @@ -891,7 +2164,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Partager le code de groupe", "PL": "TRANSLATE", "FI": "Jaa ryhmäkoodi", "ES": "Compartir código de grupo" @@ -904,7 +2177,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Code de groupe envoyé!", "PL": "TRANSLATE", "FI": "Lähetä ryhmäkoodi!", "ES": "¡Código de grupo enviado!" @@ -917,7 +2190,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "COMMENCER", "PL": "TRANSLATE", "FI": "ALOITA", "ES": "INICIAR" @@ -930,50 +2203,76 @@ "PT-BR": "Por favor escolha a primeira letra do ginásio:", "RU": "Пожалуйста, выберите первую букву гима:", "NO": "Hva er den første bokstaven i gym navnet", - "FR": "Sélectionne la première lettre de l'arène", + "FR": "Sélectionnez la première lettre de l'arène:", "PL": "Wybierz pierwszą litere nazwy Areny", "FI": "Valitse salin ensimmäinen kirjain:", "ES": "Por favor, selecciona la primera letra del gimnasio:" }, "select_gym_first_letter_or_gym_area": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", + "NL": "Selecteer de eerste letter van de gym of gym gebied:", + "DE": "Bitte Anfangsbuchstaben der Arena oder des Arena-Gebiets auswählen:", "EN": "Please select the first letter of the gym or gym area:", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Sélectionnez la première lettre de l'arène ou de la zone:", "PL": "TRANSLATE", "FI": "Valitse salin ensimmäinen kirjain tai alue:", "ES": "Seleccione la primera letra del gimnasio o la zona del gimnasio:" }, "select_gym_name_or_gym_area": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", + "NL": "Selecteer gym of gym gebied:", + "DE": "Bitte Arena oder Arena-Gebiet auswählen:", "EN": "Please select gym or gym area:", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Sélectionnez l'arène ou la zone:", "PL": "TRANSLATE", "FI": "Valitse sali tai alue:", "ES": "Seleccione gimnasio o zona de gimnasio:" }, "select_gym_area": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", + "NL": "Selecteer gym gebied:", + "DE": "Bitte Arena-Gebiet auswählen:", "EN": "Please select the gymarea:", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Sélectionnez la zone:", "PL": "TRANSLATE", "FI": "Valitse salialue:", "ES": "Por favor, selecciona la zona del gimnasio:" }, + "default_gymarea": { + "NL": "Standaard gym gebied", + "DE": "Standard Arena-Gebiet", + "EN": "Default gymarea", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Zone par défaut", + "PL": "TRANSLATE", + "FI": "Oletussalialue", + "ES": "TRANSLATE" + }, + "gymareas": { + "NL": "Gyn gebied", + "DE": "Arena-Gebiete", + "EN": "Gymareas", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Zones", + "PL": "TRANSLATE", + "FI": "Salialueet", + "ES": "TRANSLATE" + }, "select_gym": { "NL": "Beginletter geselecteerd.", "DE": "Anfangsbuchstabe ausgewählt.", @@ -982,7 +2281,7 @@ "PT-BR": "Primeira letra escolhida.:", "RU": "Первая буква вабрана.", "NO": "Første bokstav er valgt", - "FR": "Première lettre sélectionnée", + "FR": "Première lettre sélectionnée.", "PL": "Pierwsza litera wybrana", "FI": "Ensimmäinen kirjain valittu.", "ES": "Primera letra seleccionada." @@ -995,11 +2294,50 @@ "PT-BR": "A raid já existe!", "RU": "Рейд уже существует!", "NO": "Raidet finnes allerede!", - "FR": "Ce raid existe déjà", + "FR": "Ce raid existe déjà!", "PL": "Rajd już istnieje", "FI": "Raidi on jo olemassa!", "ES": "¡La incursión ya existe!" }, + "inspect_raid_or_create_event": { + "NL": "Inspect de bestaande raid of create een new event raid", + "DE": "Existierenden Raid untersuchen oder neuen Event-Raid erstellen", + "EN": "Inspect the existing raid or create a new event raid", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Afficher un raid enregistré ou créer un événement de raid", + "PL": "TRANSLATE", + "FI": "Tarkaste tallennettua raidia tai luo tapahtuma", + "ES": "TRANSLATE" + }, + "saved_raid": { + "NL": "Laat opgeslagen raid zien", + "DE": "Gespeicherten Raid anzeigen", + "EN": "Show saved raid", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Afficher le raid enregistré", + "PL": "TRANSLATE", + "FI": "Tallennettu raidi", + "ES": "TRANSLATE" + }, + "create_event_raid": { + "NL": "Maak een event raid", + "DE": "Event-Raid erstellen", + "EN": "Create event raid", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Créer un événement de raid", + "PL": "TRANSLATE", + "FI": "Luo raiditapahtuma", + "ES": "TRANSLATE" + }, "create_raid": { "NL": "Begin Raid in", "DE": "Erstelle Raid in", @@ -1008,7 +2346,7 @@ "PT-BR": "Criar raid em", "RU": "Создать рейд в", "NO": "Opprett raid i", - "FR": "Créer un raid", + "FR": "Créer un raid dans", "PL": "Utwórz Rajd w", "FI": "Luo Raidi", "ES": "Crear incursión en" @@ -1021,7 +2359,7 @@ "PT-BR": "Para continuar, escolha o nível da raid", "RU": "Чтобы продолжить, пожалуйста, укажите уровень рейда", "NO": "Velg raid level for å fortsette", - "FR": "Pour continuer, sélectionne un niveau de raid", + "FR": "Pour continuer, sélectionnez un niveau de raid", "PL": "Żeby kontynuować, wybierz poziom Rajdu", "FI": "Jatkaaksesi valitse raidin taso", "ES": "Para continuar, selecciona el nivel de incursión" @@ -1034,7 +2372,7 @@ "PT-BR": "Escolha o nível da raid", "RU": "Укажите уровень рейда", "NO": "Velg raid level", - "FR": "Sélectionne un niveau de raid", + "FR": "Sélectionnez un niveau de raid", "PL": "Wybierz poziom Rajdu", "FI": "Valitse raidin taso", "ES": "Selecciona el nivel de la incursión" @@ -1047,7 +2385,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Désactiver le niveau de raid", "PL": "TRANSLATE", "FI": "Poista raidi taso käytöstä", "ES": "Desactivar el nivel de incursión" @@ -1060,7 +2398,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Niveau de raid désactivé", "PL": "TRANSLATE", "FI": "Raidi taso ei käytössä", "ES": "Nivel de incursión desactivado" @@ -1073,7 +2411,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Informations de dresseur", "PL": "TRANSLATE", "FI": "Kouluttajatieto", "ES": "Información del entrenador" @@ -1086,7 +2424,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Vos données de dresseur:", "PL": "TRANSLATE", "FI": "Kouluttajatietosi:", "ES": "Tús datos de entrenador:" @@ -1099,7 +2437,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Partager le message de dresseur", "PL": "TRANSLATE", "FI": "Jaa kouluttajaviesti", "ES": "Compartir mensaje de entrenador" @@ -1112,7 +2450,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Supprimer le message de dresseur", "PL": "TRANSLATE", "FI": "Poista kouluttajaviesti", "ES": "Eliminar mensaje de entrenador" @@ -1125,7 +2463,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Supprimer le message de dresseur de la conversation?", "PL": "TRANSLATE", "FI": "Poista kouluttajaviesti chatista?", "ES": "¿Eliminar mensaje de entrenador del chat?" @@ -1138,7 +2476,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Message de dresseur supprimé!", "PL": "TRANSLATE", "FI": "Kouluttajaviesti poistettu!", "ES": "¡Mensaje de entrenador eliminado!" @@ -1151,7 +2489,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Vous pouvez également partager ou supprimer le message de dresseur dans une conversation.", "PL": "TRANSLATE", "FI": "Voit myos jakaa kouluttajaviestin chatin kanssa.", "ES": "También puedes compartir o eliminar el mensaje de entrenador en un chat." @@ -1164,7 +2502,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Modifiez vos informations de dresseur:", "PL": "TRANSLATE", "FI": "Aseta trainer datasi:", "ES": "Edita tus datos de entrenador:" @@ -1177,7 +2515,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Via 'Informations de dresseur' vous pouvez afficher vos données. Modifiez-les avec les boutons 'Équipe' et 'Niveau'.", "PL": "TRANSLATE", "FI": "Voit tarkastella kouluttajatietojasi 'kouluttajatieto' komennolla. Muuta tietojasi 'Joukkue' ja 'Taso' -napeilla.", "ES": "Con 'Información del entrenador' puedes ver los datos de tu entrenador. Cambia los datos con los botones 'Equipo' y 'Nivel'." @@ -1190,7 +2528,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Une fois terminé, cliquez à nouveau sur 'Informations de dresseur'. Les données de votre dresseur seront à nouveau masquées.", "PL": "TRANSLATE", "FI": "Kun olet valmis, paina uudelleen 'Kouluttajatieto'. Kouluttajatietosi ovat jälleen piilossa.", "ES": "Una vez que termines, presiona nuevamente en 'Información del entrenador'. Los datos de tu entrenador se volverán a ocultar." @@ -1203,7 +2541,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Partagez dans une conversation le message pour afficher et modifier les informations de dresseur:", "PL": "TRANSLATE", "FI": "Jaa viesti kouluttajatietojen näyttämistä ja muuttamista varten chatin kanssa:", "ES": "Comparte el mensaje para mostrar y editar los datos del entrenador con un chat:" @@ -1216,46 +2554,46 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Aucune conversation trouvée! Impossible de partager le message pour afficher et modifier les informations de dresseur!", "PL": "TRANSLATE", "FI": "Chatteja ei löytynyt! Et voi jakaa viestiä tietojen näyttämiseen ja muuttamiseen!", "ES": "¡No se encontraron chats! ¡No se puede compartir el mensaje para mostrar y editar los datos del entrenador!" }, "trainername": { "NL": "Trainernaam", - "DE": "TRANSLATE", + "DE": "Trainername", "EN": "Trainername", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Nom de dresseur", "PL": "TRANSLATE", "FI": "Pelinimi", "ES": "Nombre de entrenador" }, "trainername_add": { "NL": "Trainernaam toevoegen", - "DE": "TRANSLATE", + "DE": "Trainername hinzufügen", "EN": "Add trainername", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Ajouter le nom de dresseur", "PL": "TRANSLATE", "FI": "Lisää pelinimi", "ES": "Añadir nombre de entrenador" }, "trainername_edit": { "NL": "Trainernaam aanpassen", - "DE": "TRANSLATE", + "DE": "Trainername bearbeiten", "EN": "Edit trainername", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Modifier le nom de dresseur", "PL": "TRANSLATE", "FI": "Muokkaa pelinimeä", "ES": "Editar nombre de entrenador" @@ -1268,7 +2606,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Entrez votre nom de dresseur", "PL": "TRANSLATE", "FI": "Kirjoita pelinimesi:", "ES": "Escribe tu nombre de entrenador" @@ -1281,7 +2619,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Merci. Votre nom est désormais:", "PL": "TRANSLATE", "FI": "Kiitos. Pelinimesi on nyt:", "ES": "Gracias. Ahora tu nombre es:" @@ -1294,20 +2632,20 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Veuillez utiliser uniquement des caractères alphanumériques. Réessayez", "PL": "TRANSLATE", "FI": "Pelinimessä voi olla pelkästään numeroita ja kirjaimia. Yritä uudelleen.", "ES": "Por favor, usa solo caracteres alfanuméricos. Intentalo otra vez" }, "switch_display_name": { "NL": "Wissel display naam", - "DE": "TRANSLATE", + "DE": "Anzeigename wechseln", "EN": "Switch display name", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Modifier l'affichage du nom", "PL": "TRANSLATE", "FI": "Vaihda näyttönimeä", "ES": "Cambiar nombre para mostrar" @@ -1320,7 +2658,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Code de dresseur", "PL": "TRANSLATE", "FI": "Kaverikoodi", "ES": "Código de entrenador" @@ -1333,7 +2671,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "indéfini", "PL": "TRANSLATE", "FI": "Ei asetettu", "ES": "no establecido" @@ -1346,7 +2684,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Entrez votre code de dresseur", "PL": "TRANSLATE", "FI": "Kirjoita kaverikoodisi:", "ES": "Pon tu código de entrenador" @@ -1359,7 +2697,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Merci. Votre code de dresseur est désormais:", "PL": "TRANSLATE", "FI": "Kiitos. Kaverikoodisi on nyt:", "ES": "Gracias. Ahora tu código de entrenador es:" @@ -1372,72 +2710,72 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Votre code de dresseur ne contient pas 12 chiffres. Réessayez", "PL": "TRANSLATE", "FI": "Annetussa kaverikoodissa ei ole 12 numeroa. Yritä uudelleen.", "ES": "El código de entrenador que pustiste no tiene 12 numeros. Intentalo otra vez" }, "trainer_not_found": { "NL": "Trainer niet gevonden!", - "DE": "TRANSLATE", + "DE": "Trainer nicht gefunden!", "EN": "Trainer not found!", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Dresseur introuvable!", "PL": "TRANSLATE", "FI": "Kouluttajaa ei löytynyt!", "ES": "¡Entrenador no encontrado!" }, "bot_lang": { "NL": "Bot taal", - "DE": "TRANSLATE", + "DE": "Bot-Sprache", "EN": "Bot language", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Langue du bot", "PL": "TRANSLATE", "FI": "Botin kieli", "ES": "Idioma del bot" }, "change_lang": { "NL": "Verander de bot taal", - "DE": "TRANSLATE", + "DE": "Bot-Sprache ändern", "EN": "Change the bot's language", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Modifiez la langue du bot", "PL": "TRANSLATE", "FI": "Vaihda botin kieli", "ES": "Cambiar el idioma del bot" }, "new_lang_saved": { "NL": "Nieuwe taal opgeslagen!", - "DE": "TRANSLATE", + "DE": "Neue Sprache gespeichert!", "EN": "New language saved!", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Nouvelle langue enregistrée!", "PL": "TRANSLATE", "FI": "Kieli vaihdettu!", "ES": "¡Nuevo idioma guardado!" }, "display_name_explanation": { "NL": "De pijl (->) geeft aan welke naam er in de raid poll wordt gebruikt. Je kan dit aanpassen in de instellingen.", - "DE": "TRANSLATE", + "DE": "Der Pfeil (->) zeigt deinen Namen für Raid-Abstimmungen. Du kannst deinen Anzeigenamen in den Einstellungen anpassen.", "EN": "The arrow (->) marks the name displayed in raid polls. You can change your displayname from name settings.", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "La flèche (->) indique quel nom est affiché dans les annonces de raid. Vous pouvez modifier le nom dans les paramètres.", "PL": "TRANSLATE", "FI": "Nuoli (->) osoittaa raidi-ilmoituksissa käytettävää nimeä. Voit vaihtaa näyttönimeä nimiasetuksista.", "ES": "La flecha (->) indica el nombre que se muestra en los anuncios de incursiones. Puedes cambiar el nombre en los ajustes." @@ -1450,7 +2788,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Nom", "PL": "TRANSLATE", "FI": "Nimi", "ES": "Nombre" @@ -1463,7 +2801,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Équipe", "PL": "TRANSLATE", "FI": "Joukkue", "ES": "Equipo" @@ -1476,7 +2814,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Équipe enregistrée!", "PL": "TRANSLATE", "FI": "Joukkue tallennettu!", "ES": "¡Equipo guardado!" @@ -1489,7 +2827,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Sélectionnez votre équipe:", "PL": "TRANSLATE", "FI": "Valitse joukkueesi:", "ES": "Selecciona tu equipo:" @@ -1502,7 +2840,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Niveau", "PL": "TRANSLATE", "FI": "Taso", "ES": "Nivel" @@ -1515,7 +2853,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Niveau enregistré!", "PL": "TRANSLATE", "FI": "Taso tallennettu!", "ES": "¡Nivel guardado!" @@ -1528,7 +2866,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Sélectionnez votre niveau:", "PL": "TRANSLATE", "FI": "Valitse tasosi:", "ES": "Selecciona tu nivel:" @@ -1554,7 +2892,7 @@ "PT-BR": "Ginásio salvo.", "RU": "Гим сохранен", "NO": "Gym lagret", - "FR": "Arène sauvegardée", + "FR": "Arène enregistrée.", "PL": "Arena zapisana", "FI": "Sali tallennettu.", "ES": "Gimnasio guardado." @@ -1567,7 +2905,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Arène masquée", "PL": "TRANSLATE", "FI": "Piilotettu sali", "ES": "Gimnasio oculto" @@ -1580,7 +2918,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Arènes masquées", "PL": "TRANSLATE", "FI": "Piilotetut salit", "ES": "Gimnasios ocultos" @@ -1593,7 +2931,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Aucune arène masquée trouvée!", "PL": "TRANSLATE", "FI": "Piilotettuja saleja ei löytynyt!", "ES": "¡No se encontraron gimnasios ocultos!" @@ -1606,7 +2944,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Afficher l'arène", "PL": "TRANSLATE", "FI": "Näytä sali", "ES": "Ver gimnasio" @@ -1619,7 +2957,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Masquer l'arène", "PL": "TRANSLATE", "FI": "Piilota sali", "ES": "Ocultar gimnasio" @@ -1632,7 +2970,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Arène normale", "PL": "TRANSLATE", "FI": "Tavallinen sali", "ES": "Gimnasio normal" @@ -1645,7 +2983,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Arène EX", "PL": "TRANSLATE", "FI": "Ex-raidi sali", "ES": "Gimnasio EX" @@ -1658,20 +2996,20 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Informations détaillées de l'arène:", "PL": "TRANSLATE", "FI": "Salin lisätiedot:", "ES": "Detalles ampliados del gimnasio:" }, "change_extended_gym_details": { "NL": "Verander de extra gym informatie?", - "DE": "Erweiterte Arena Details ändern?", + "DE": "Erweiterte Arena Details speichern?", "EN": "Save extended gym details?", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Enregistrer les informations détaillées de l'arène?", "PL": "TRANSLATE", "FI": "Tallenna salin lisätiedot?", "ES": "¿Guardar detalles ampliados del gimnasio?" @@ -1684,7 +3022,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Nouvelles informations détaillées de l'arène:", "PL": "TRANSLATE", "FI": "Salin uudet lisätiedot:", "ES": "Nuevo detalle ampliado del gimnasio:" @@ -1697,36 +3035,49 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Erreur! Arène introuvable!", "PL": "TRANSLATE", "FI": "Virhe! Salia ei löytynyt!", "ES": "¡Error! ¡Gimnasio no encontrado!" }, - "gym_coordinates_format_error": { - "NL": "Error! Stuur de coordinaten in het volgende formaat Latitude,Longitude om de nieuwe gym toe te voegen!", - "DE": "Fehler! Bitte übermittle die Koordinaten im Format Breitengrad,Längengrad zum Hinzufügen einer neuen Arena!", - "EN": "Error! Please submit the coordinates in the format Latitude,Longitude to add a new gym!", + "gym_coordinates": { + "NL": "Coordinaten", + "DE": "Koordinaten", + "EN": "Coordinates", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Coordonnées", "PL": "TRANSLATE", - "FI": "Virhe! Anna koordinaatit muotossa Leveysaste,Pituusaste lisätäksesi uuden salin!", - "ES": "¡Error! ¡Envía las coordenadas en el formato latitud,longitud para agregar un nuevo gimnasio!" + "FI": "Koordinaatit", + "ES": "TRANSLATE" + }, + "gym_edit_coordinates": { + "NL": "Wijzig coordinaten", + "DE": "Koordinaten bearbeiten", + "EN": "Edit coordinates", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Modifier les coordonnées", + "PL": "TRANSLATE", + "FI": "Muokkaa koordinaatteja", + "ES": "TRANSLATE" }, - "gym_coordinates_format_example": { - "NL": "Als voorbeeld: /addgym 51.516263,4.377755", - "DE": "Zum Beispiel: /addgym 52.516263,13.377755", - "EN": "For example: /addgym 52.516263,13.377755", + "gym_coordinates_format_error": { + "NL": "Error! Stuur de coordinaten in het volgende formaat Latitude,Longitude om de nieuwe gym toe te voegen!", + "DE": "Fehler! Bitte übermittle die Koordinaten im Format Breitengrad,Längengrad zum Hinzufügen einer neuen Arena!", + "EN": "Error! Please submit the coordinates in the format Latitude,Longitude to add a new gym!", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Erreur! Insérez les coordonnées au format Latitude,Longitude pour ajouter une nouvelle arène!", "PL": "TRANSLATE", - "FI": "Esimerkiksi: /addgym 52.516263,13.377755", - "ES": "Por ejemplo: /addgym 52.516263,13.377755" + "FI": "Virhe! Anna koordinaatit muotossa Leveysaste,Pituusaste lisätäksesi uuden salin!", + "ES": "¡Error! ¡Envía las coordenadas en el formato latitud,longitud para agregar un nuevo gimnasio!" }, "gym_gps_coordinates_format_error": { "NL": "Error! Stuur de coordinaten in het volgende formaat: Latitude,Longitude", @@ -1736,62 +3087,36 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Erreur! Inscrivez les coordonnées au format suivant: Latitude,Longitude", "PL": "TRANSLATE", - "FI": "Virhe! Virhe! Anna koordinaatit seuraavassa muodossa: Leveysaste,Pituusaste", + "FI": "Virhe! Anna koordinaatit seuraavassa muodossa: Leveysaste,Pituusaste", "ES": "¡Error! Envía las coordenadas en el siguiente formato: latitud,longitud" }, "gym_gps_instructions": { - "NL": "Om de gym coordinaten te veranderen stuur /gymgps gym id, nieuwe gym coordinaten!", - "DE": "Zum Ändern der Arena-Koordinaten bitte /gymgps Arena-ID, neue Arena-Koordinaten eingeben!", - "EN": "To change the coordinates of a gym please enter /gymgps gym id, new gym coordinates!", + "NL": "Stuur me de gym coordinaten in de volgende format: Latitude,Longitude", + "DE": "Bitte die Arena-Koordinaten in folgendem Format senden: Latitude,Longitude", + "EN": "Send me the gym coordinates in the following format: Latitude,Longitude", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Envoyez-moi les coordonnées de l'arène au format suivant: Latitude,Longitude", "PL": "TRANSLATE", - "FI": "Muuttaaksesi salin koordinaatteja komenna gymgps salin id, uudet koordinaatit!", - "ES": "¡Para cambiar las coordenadas de un gimnasio, ingresa /gymgps gym id, nuevas coordenadas de gimnasio!" + "FI": "Lähetä minulle salin koordinaatit muodossa: Leveysaste,Pituusaste", + "ES": "TRANSLATE" }, "gym_gps_example": { - "NL": "Als voorbeeld: /gymgps 34, 51.516263,4.377755", - "DE": "Zum Beispiel: /gymgps 34, 52.516263,13.377755", - "EN": "For example: /gymgps 34, 52.516263,13.377755", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Esimerkiksi: /gymgps 34, 52.516263,13.377755", - "ES": "Por ejemplo: /gymgps 34, 52.516263,13.377755" - }, - "gym_gps_added": { - "NL": "Gym coordinaten succesvol toegevoegd!", - "DE": "Arena-Koordinaten erfolgreich hinzugefügt!", - "EN": "Gym coordinates successfully added!", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Salin koordinaatit lisätty onnistuneesti!", - "ES": "¡Se agregaron con éxito las coordenadas del gimnasio!" - }, - "gym_id_gps_missing": { - "NL": "Error! gym id of coordinaten mist!", - "DE": "Fehler! Arena-ID oder Koordinaten fehlen!", - "EN": "Error! Gym id or coordinates are missing!", + "NL": "Als voorbeeld: 51.516263,4.377755", + "DE": "Zum Beispiel: 52.516263,13.377755", + "EN": "For example: 52.516263,13.377755", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Par exemple: 52.516263,13.377755", "PL": "TRANSLATE", - "FI": "Virhe! Salin id tai koordinaatit puuttuvat!", - "ES": "¡Error! ¡Faltan el gym id o las coordenadas!" + "FI": "Esimerkiksi: 52.516263,13.377755", + "ES": "Por ejemplo: 52.516263,13.377755" }, "gymname_then_location": { "NL": "Gebruik het commando /gymname Naam van de gym om deze aan de coordinaten vast te zetten. Daarna kan je weer een locatie delen!", @@ -1801,7 +3126,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Utilisez la commande /gymname Nom de l'arène pour définir un nom d'arène pour les coordonnées soumises précédemment. Vous pourrez ensuite envoyer une nouvelle position!", "PL": "TRANSLATE", "FI": "Käytä komentoa /gymname Salin nimi asettaaksesi salinimen aiemmin annetuille koordinaateille. Sitten voit antaa jälleen sijainnin!", "ES": "Utiliza el comando /gymname Nombre del gimnasio para establecer un nombre de gimnasio para las coordenadas enviadas recientemente. ¡Entonces podrás volver a enviar una ubicación!" @@ -1814,11 +3139,24 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Arène ajoutée avec succès!", "PL": "TRANSLATE", "FI": "Sali lisätty onnistuneesti!", "ES": "¡Se agregó con éxito el gimnasio!" }, + "gym_create": { + "NL": "Maak een nieuwe gym", + "DE": "Neue Arena erstellen", + "EN": "Create a new gym", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Créer une nouvelle arène", + "PL": "TRANSLATE", + "FI": "Luo uusi sali", + "ES": "TRANSLATE" + }, "gym_updated": { "NL": "De gym is succesvol aangepast!", "DE": "Arena erfolgreich aktualisiert!", @@ -1827,7 +3165,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Arène mise à jour avec succès!", "PL": "TRANSLATE", "FI": "Sali päivitetty onnistuneesti!", "ES": "¡Se actualizó con éxito el gimnasio!" @@ -1840,7 +3178,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Supprimer l'arène", "PL": "TRANSLATE", "FI": "Poista sali", "ES": "Eliminar gimnasio" @@ -1853,7 +3191,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Vraiment supprimer cette arène?", "PL": "TRANSLATE", "FI": "Vahvista salin poisto?", "ES": "¿De verdad eliminar este gimnasio?" @@ -1866,7 +3204,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Arène supprimée avec succès!", "PL": "TRANSLATE", "FI": "Sali poistettu onnistuneesti!", "ES": "¡Se eliminó con éxito este gimnasio!" @@ -1879,7 +3217,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Détails de l'arène", "PL": "TRANSLATE", "FI": "Salin yksityiskohdat", "ES": "Detalles del gimnasio" @@ -1892,205 +3230,114 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Afficher les détails de l'arène", "PL": "TRANSLATE", "FI": "Näytä salin yksityiskohdat", "ES": "Ver detalles del gimnasio" }, - "gym_note_added": { - "NL": "Gym info toegevoegd!", - "DE": "Arena-Notiz erfolgreich hinzugefügt!", - "EN": "Gym note successfully added!", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Salin muistiinpano tallennettu onnistuneesti!", - "ES": "¡Nota del gimnasio añadida con éxito!" - }, - "gym_note_deleted": { - "NL": "Gym info verwijderd!", - "DE": "Arena-Notiz erfolgreich gelöscht!", - "EN": "Gym note successfully deleted!", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "alin muistiinpano poistettu onnistuneesti!", - "ES": "¡Nota del gimnasio eliminada con éxito!" - }, - "gym_note_new": { - "NL": "Nieuwe gym info:", - "DE": "Neue Arena-Notiz:", - "EN": "New gym note:", + "gym_edit_text_too_long": { + "NL": "Error! niet meer dan 255 tekens!", + "DE": "Fehler! Es sind maximal 255 Zeichen erlaubt!", + "EN": "Error! The maximum text length is 255 characters!", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Erreur! La longueur maximale du texte est de 255 caractères!", "PL": "TRANSLATE", - "FI": "Uusi sali muistiinpano:", - "ES": "Nueva nota del gimnasio:" + "FI": "Virhe! Tekstin enimmäispituus on 255 merkkiä!", + "ES": "TRANSLATE" }, - "gym_id_note_missing": { - "NL": "Error! Gym id of info mist!", - "DE": "Fehler! Arena-ID oder Notiz fehlt!", - "EN": "Error! Gym id or note is missing!", + "gym_add_edit_note": { + "NL": "gym note", + "DE": "Arena-Notiz", + "EN": "gym note", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Notice d'arène", "PL": "TRANSLATE", - "FI": "Virhe! Salin id tai muistiinpano puuttuu!", - "ES": "¡Error! ¡Faltan gym id o nota!" + "FI": "lisätietoja", + "ES": "TRANSLATE" }, "gym_note_instructions": { - "NL": "Om de gym info te veranderen stuur /gymnote gym id, nieuwe gym info!", - "DE": "Zum Ändern einer Arena-Notiz bitte /gymnote Arena-ID, neue Arena-Notiz eingeben!", - "EN": "To change the note of a gym please enter /gymnote gym id, new gym note!", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Vaihtaaksesi salin muistiinpanoa komenna /gymnote salin id, uusi muistiinpano!", - "ES": "¡Para cambiar la nota de un gimnasio, escribe /gymnote gym id, nueva nota de gimnasio!" - }, - "gym_note_example": { - "NL": "Als voorbeeld: /gymnote 34, ontmoetingsplek: achter het gebouw!", - "DE": "Zum Beispiel: /gymnote 34, Treffpunkt: Hinter dem Gebäude", - "EN": "For example: /gymnote 34, Meeting point: Behind the building!", + "NL": "Stuur me een nieuwe gym notitie (maximaal 255 tekens):", + "DE": "Neue Arena-Notiz eingeben (max. 255 Zeichen)", + "EN": "Send me the new gym note (max 255 characters):", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Envoyez-moi la nouvelle notice d'arène (max 255 caractères):", "PL": "TRANSLATE", - "FI": "Esimerkiksi: /gymnote 34, Tapaamispiste: Rakennuksen takana!", - "ES": "Por ejemplo: /gymnote 34, Meeting point: Behind the building" + "FI": "Lähetä minulle salin uudet lisätiedot (max 255 merkkiä):", + "ES": "TRANSLATE" }, - "gym_note_reset": { - "NL": "Om de gym info te verwijderen stuur /gymnote gym id, reset.", - "DE": "Zum Löschen einer Arena-Notiz /gymnote Arena-ID, reset eingeben.", - "EN": "To delete a gym note enter /gymnote gym id, reset.", + "gym_address": { + "NL": "Gym adres", + "DE": "Arena-Adresse", + "EN": "gym address", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Adresse de l'arène", "PL": "TRANSLATE", - "FI": "Poistaaksesi salin muistiinpanon komenna /gymnote salin id, reset.", - "ES": "Para eliminar la nota de un gimnasio, escribe /gymnote gym id, reset." + "FI": "salin osoite", + "ES": "TRANSLATE" }, - "gym_note_reset_example": { - "NL": "Als voorbeeld: /gymnote 34, reset.", - "DE": "Zum Beispiel: /gymnote 34, reset", - "EN": "For example: /gymnote 34, reset", + "gym_stored_address": { + "NL": "Opgeslagen adres", + "DE": "Gespeicherte Adresse", + "EN": "Stored address", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Adresse enregistrée", "PL": "TRANSLATE", - "FI": "Esimerkiksi: /gymnote 34, reset", - "ES": "Por ejemplo: /gymnote 34, reset" + "FI": "Tallennettu osoite", + "ES": "TRANSLATE" }, "gym_address_instructions": { - "NL": "Om het adres aan te passen stuur /gymaddress gym id, het nieuwe gym adres!", - "DE": "Zum Ändern einer Arena-Adresse bitte /gymaddress Arena-ID, neue Arena-Adresse eingeben!", - "EN": "To change the address of a gym please enter /gymaddress gym id, new gym address!", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Vaihtaaksesi salin osoitetta komenna /gymaddress salin id, uusi salin osoite!", - "ES": "¡Para cambiar la dirección de un gimnasio, escribe /gymaddress gym id, nueva dirección de gimnasio!" - }, - "gym_address_example": { - "NL": "Als voorbeeld /gymaddress 34, Markt 61, 3131 CR Vlaardingen !", - "DE": "Zum Beispiel: /gymaddress 34, Großer Stern, 10557 Berlin", - "EN": "For example: /gymaddress 34, Großer Stern, 10557 Berlin!", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Esimeriksi: /gymaddress 34, Mannerheiminkatu, 49400 Hamina!", - "ES": "Por ejemplo: /gymaddress 34, Großer Stern, 10557 Berlin" - }, - "gym_address_reset": { - "NL": "Om het gym adres te verwijderen stuur /gymaddress gym id, reset.", - "DE": "Zum Löschen einer Arena-Adresse /gymaddress Arena-ID, reset eingeben.", - "EN": "To delete a gym address enter /gymaddress gym id, reset.", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Poistaaksesi salin osoitteen komenna /gymaddress salin id, reset.", - "ES": "Para eliminar la dirección de un gimnasio, escribe /gymaddress gym id, reset." - }, - "gym_address_reset_example": { - "NL": "Als voorbeeld /gymaddress 34, reset", - "DE": "Zum Beispiel: /gymaddress 34, reset", - "EN": "For example: /gymaddress 34, reset", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Esimerkiksi: /gymaddress 34, reset", - "ES": "Por ejemplo: /gymaddress 34, reset" - }, - "gym_address_added": { - "NL": "Gym adres succesvol toegevoegd!", - "DE": "Arena-Adresse erfolgreich hinzugefügt!", - "EN": "Gym address successfully added!", + "NL": "Stuur me het nieuwe gym adres:", + "DE": "Neue Arena-Adresse eingeben:", + "EN": "Send me the new gym address:", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Envoyez-moi l'adresse de la nouvelle arène:", "PL": "TRANSLATE", - "FI": "Salin osoite tallennettu onnistuneesti!", - "ES": "¡Dirección del gimnasio añadida con éxito!" + "FI": "Lähetä minulle salin uusi osoite:", + "ES": "TRANSLATE" }, - "gym_id_address_missing": { - "NL": "Error! gym id of adres mist!", - "DE": "Fehler! Arena-ID oder Adresse fehlt!", - "EN": "Error! Gym id or address is missing!", + "gym_address_lookup_result": { + "NL": "Opgezochte adres", + "DE": "Ergebnis der Adresssuche", + "EN": "Address lookup result", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Résultat de la recherche d'adresse", "PL": "TRANSLATE", - "FI": "Virhe! Salin id tai osoite puuttuu!", - "ES": "¡Error! Faltan gym id o dirreción!" + "FI": "Osoitehaun tulos", + "ES": "TRANSLATE" }, - "gym_address_deleted": { - "NL": "Gym adres succesvol verwijderd!", - "DE": "Arena-Adresse erfolgreich gelöscht!", - "EN": "Gym address successfully deleted!", + "gym_save_lookup_result": { + "NL": "Sla opgezochte resultaat op", + "DE": "Ergebnis der Adresssuche speichern", + "EN": "Save lookup result", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Enregistrer le résultat de la recherche d'adresse", "PL": "TRANSLATE", - "FI": "Salin osoite poistettu onnistuneesti!", - "ES": "¡Dirección del gimnasio eliminada con éxito!" + "FI": "Tallenna osoitehaun tulos", + "ES": "TRANSLATE" }, "select_raid_boss": { "NL": "Selecteer Raid baas", @@ -2100,7 +3347,7 @@ "PT-BR": "Escolha o Pokémon chefe da raid", "RU": "Выберите Рейд босса", "NO": "Velg raid boss", - "FR": "Sélectionne un boss de raid", + "FR": "Sélectionnez un boss de raid", "PL": "Wybierz Bossa Rajdu", "FI": "Valitse raidibossi", "ES": "Selecciona el jefe de incursión" @@ -2113,7 +3360,7 @@ "PT-BR": "Escolha o Pokémon", "RU": "Укажите Покемона", "NO": "Velg Pokemon", - "FR": "Sélectionne un Pokémon", + "FR": "Sélectionnez un Pokémon", "PL": "Wybierz Pokemona", "FI": "Valitse Pokemoni", "ES": "Selecciona Pokémon" @@ -2126,11 +3373,24 @@ "PT-BR": "Raid salva:", "RU": "Рейд сохранен:", "NO": "Raid lagret", - "FR": "Raid sauvegardé", + "FR": "Raid enregistré:", "PL": "Rajd zapisany", "FI": "Raidi tallennettu:", "ES": "Incursión guardada:" }, + "featured_pokemon": { + "NL": "Pokemon", + "DE": "Rampenlicht-Pokemon", + "EN": "Featured Pokemon", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Pokémon vedette", + "PL": "TRANSLATE", + "FI": "Tapahtuman Pokemon", + "ES": "TRANSLATE" + }, "raid_boss": { "NL": "Raid baas", "DE": "Raid-Boss", @@ -2152,7 +3412,7 @@ "PT-BR": "Chefe da raid salvo!", "RU": "Рейд Босс сохранен!", "NO": "Raid boss lagret!", - "FR": "Boss de raid sauvegardé", + "FR": "Boss de raid enregistré!", "PL": "Boss Rajdu zapisany!", "FI": "Raidibossi tallennettu!", "ES": "¡Jefe de incursión guardado!" @@ -2165,7 +3425,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Boss de raid exclus de l'importation:", "PL": "TRANSLATE", "FI": "Tuonnista poissuljetut raidibossit:", "ES": "Jefes de incursión excluidos de importación:" @@ -2178,7 +3438,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Sélectionnez le boss de raid à exclure de l'importation ou terminez l'importation avec 'Enregistrer'", "PL": "TRANSLATE", "FI": "Valitse tuonnista poissuljettavat raidibossit tai lopeta tuonti valitsemalla 'Tallenna'", "ES": "Selecciona el jefe de incursión a excluir de la importación o finaliza la importación con 'Guardar'" @@ -2191,7 +3451,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Importer les boss de raid?", "PL": "TRANSLATE", "FI": "Tuo raidibossit?", "ES": "¿Importar jefes de incursión?" @@ -2204,7 +3464,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Importation terminée!", "PL": "TRANSLATE", "FI": "Tuonti valmis!", "ES": "¡Importación finalizada!" @@ -2217,7 +3477,7 @@ "PT-BR": "Opcional - Nome do gínasio e nome do time", "RU": "Опционально - Название Гима и Команда Гима", "NO": "Valgfri - Gym navn og gym team:", - "FR": "Optionnel - Couleur de l'arène", + "FR": "Optionnel - Nom et équipe de l'arène:", "PL": "Opcjonalnie - Nazwa Areny i kolor Areny", "FI": "Valinnainen - Salin nimi ja salin joukkue:", "ES": "Opcional - Nombre del gimnasio y equipo del gimnasio:" @@ -2235,32 +3495,6 @@ "FI": "/gymname Salin nimi", "ES": "/gymname Nombre del gimnasio" }, - "set_gym_team": { - "NL": "Optioneel - zet Gym en Team", - "DE": "Optional - Arena Team setzen", - "EN": "Optional - set Gym and Team", - "IT": "Opzionale - imposta Palestra e Team", - "PT-BR": "Opcional - escolha o gínasio e time", - "RU": "Опционально - указать Гим и Команду", - "NO": "Valgfri - Fortell hvilken Gym og Team", - "FR": "Optionnel - choisir la couleur de l'arène", - "PL": "Opcjonalnie - Wybierz kolor Areny", - "FI": "Valinnainen - aseta sali ja joukkue", - "ES": "Opcional - Poner gimnasio y equipo" - }, - "set_gym_team_command": { - "NL": "/team Mystic/Valor/Instinct/Blauw/Rood/Geel", - "DE": "/team Mystic/Valor/Instinct/Blau/Rot/Gelb", - "EN": "/team Mystic/Valor/Instinct/Blue/Red/Yellow", - "IT": "/team Saggezza/Valore/Istinto/Blu/Rosso/Giallo", - "PT-BR": "/team Mystic/Valor/Instinct/Azul/Vermelho/Amarelo", - "RU": "/team Mystic/Valor/Instinct/Синий/Красный/Желтый", - "NO": "/team Mystic/Valor/Instinct/Blue/Red/Yellow", - "FR": "/team Sagesse/Bravoure/Intuition/Bleu/Rouge/Jaune", - "PL": "/team Niebieski/Czerowny/Żółty", - "FI": "/team Mystic/Valor/Instinct/Blue/Red/Yellow", - "ES": "/team Sabiduría/Valor/Instinto/Azul/Rojo/Amarillo" - }, "mystic": { "NL": "Mystic/Blauw", "DE": "Weisheit/Blau", @@ -2308,7 +3542,7 @@ "PT-BR": "Hora de começar determinada para", "RU": "Установить время начала", "NO": "Startid satt til", - "FR": "Heure de début réglée à", + "FR": "Heure de commencement réglée à", "PL": "Czas startu ustawiony na", "FI": "Aloitusaika asetettu klo", "ES": "Hora de inicio establecida en" @@ -2334,7 +3568,7 @@ "PT-BR": "Select the date of the raid:", "RU": "Указать дату рейда:", "NO": "Velg dato for raidet:", - "FR": "Sélectionne la date du raid", + "FR": "Sélectionnez la date du raid:", "PL": "Wybierz date Rajdu", "FI": "Valitse raidin päivämäärä:", "ES": "Selecciona la fecha de la incursión:" @@ -2347,7 +3581,7 @@ "PT-BR": "Select the hour of the raid:", "RU": "Указать час рейда:", "NO": "Velg tid>/b> for raidet:", - "FR": "Sélectionne l'heure du raid", + "FR": "Sélectionnez l'heure du raid:", "PL": "Wybierz godzinę rajdu:", "FI": "Valitse raidin tunti:", "ES": "Selecciona la hora de la incursión:" @@ -2360,7 +3594,7 @@ "PT-BR": "Select the start time of the raid:", "RU": "Указать Время начала рейда:", "NO": "Velg når raidet starter:", - "FR": "Sélectionne l'heure de POP du raid", + "FR": "Sélectionnez l'heure de commencement du raid:", "PL": "WYbierz czas startu Rajdu", "FI": "Valitse raidin aloitusaika:", "ES": "Selecciona la hora de inicio de la incursión:" @@ -2399,7 +3633,7 @@ "PT-BR": "Quando a raid irá começar?", "RU": "Когда начало рейда?", "NO": "Når starter raidet?", - "FR": "Quand démarre le raid ?", + "FR": "Quand commence le raid?", "PL": "Kiedy Rajd się zaczyna?", "FI": "Mihin aikaan raidi alkaa?", "ES": "¿Cuando va a empezar la incursión?" @@ -2412,7 +3646,7 @@ "PT-BR": "Em quantos minutos a raid irá começar?", "RU": "Через сколько минут рейд начинается?", "NO": "Hvor mange minutter til raidet starter?", - "FR": "Dans combien de minutes le raid commence ?", + "FR": "Dans combien de minutes le raid commence?", "PL": "Za ile minut zacznie się Rajd?", "FI": "Monenko minuutin päästä raidi alkaa?", "ES": "¿En cuantos minutos va empezar la incursión?" @@ -2425,7 +3659,7 @@ "PT-BR": "Ver horário", "RU": "Просмотр времени", "NO": "Klokkevisning", - "FR": "Voir l'heure", + "FR": "Vue horaire", "PL": "Widok zegara", "FI": "Kellonaika näkymä", "ES": "Ver horario" @@ -2438,7 +3672,7 @@ "PT-BR": "Ver minutos", "RU": "Просмотр минут", "NO": "Minuttvisning", - "FR": "Voir les minutes", + "FR": "Afficher les minutes", "PL": "Pokaż minuty", "FI": "Minuutti näkymä", "ES": "Ver minutos" @@ -2451,7 +3685,7 @@ "PT-BR": "Visualização mudou!", "RU": "Просмотр изменен!", "NO": "Visning endret!", - "FR": "L'affichage du temps à changé !", + "FR": "La vue horaire a changé!", "PL": "Widok zmieniony!", "FI": "Näkymä vaihdettu!", "ES": "¡Vista cambiada!" @@ -2464,7 +3698,7 @@ "PT-BR": "A raid já está ativa!", "RU": "Рейд уже активен!", "NO": "Raidet er allerede aktivt!", - "FR": "Le raid est déjà en cours", + "FR": "Le raid est déjà en cours!", "PL": "Rajd się już zaczął", "FI": "Raidi on jo aktiivinen!", "ES": "¡Ya está activa!" @@ -2477,7 +3711,7 @@ "PT-BR": "Você não tem permissão para editar esta raid!", "RU": "Вы не можете редактировать этот рейд!", "NO": "Du har ikke tillatelse til å redigere dette raidet!", - "FR": "Tu n'es pas autorisé à modifier ce raid", + "FR": "Vous n'êtes pas autorisé à modifier ce raid!", "PL": "Nie masz uprawnień aby edytować Rajd!", "FI": "Et voi muuttaa tätä raidia!", "ES": "¡No tienes permiso para editar esta incursión!" @@ -2490,7 +3724,7 @@ "PT-BR": "Nenhuma raid ativa encontrada no sistema!", "RU": "Не найдено активных рейдов в системе!", "NO": "Ingen aktive raid i systemet!", - "FR": "Aucun raid en cours trouvé dans le système", + "FR": "Aucun raid en cours n'a été trouvé dans le système!", "PL": "W tym momencie nie ma żadnych aktywnych Rajdów w systemie", "FI": "Järjestelmästä ei löytynyt aktiivisia raideja!", "ES": "¡No se han encontrado incursiones activas en el sistema!" @@ -2503,7 +3737,7 @@ "PT-BR": "Nenhuma das raids ativas foi compartilhada!", "RU": "Активными рейдами не делились!", "NO": "Ingen av de aktive raidene ble delt!", - "FR": "Aucun raid en cours n'a été partagé", + "FR": "Aucun raid en cours n'a été partagé!", "PL": "Żaden z aktywnych rajdów nie został udostępniony!", "FI": "Yhtäkään aktiivista raidia ei ole jaettu!", "ES": "¡Ninguna de las incursiones activas fue compartida!" @@ -2516,7 +3750,7 @@ "PT-BR": "Nenhuma raid ativa no momento!", "RU": "Нету текущих активных рейдов!", "NO": "Det er ingen aktive raid akkurat nå!", - "FR": "Aucun raid en cours", + "FR": "Aucun raid en cours actuellement!", "PL": "Brak aktywnych Rajdów w tej chwili!", "FI": "Ei aktiivisia raideja tällä hetkellä!", "ES": "¡Actualmente no hay incursiones activas!" @@ -2529,7 +3763,7 @@ "PT-BR": "Pokemon salvo ", "RU": "Покемон сохранен:", "NO": "Pokemon lagret:", - "FR": "Pokémon enregistré", + "FR": "Pokémon enregistré: ", "PL": "Pokemon zapisany:", "FI": "Pokemoni tallennettu:", "ES": "Pokémon guardado:" @@ -2542,7 +3776,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Modifier Pokémon", "PL": "TRANSLATE", "FI": "Muuta Pokemonia", "ES": "Editar Pokémon" @@ -2555,7 +3789,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Afficher, modifier et importer des boss de raid", "PL": "TRANSLATE", "FI": "Listaa, muuta ja tuo raidibosseja", "ES": "Ver, editar e importar jefes de incursión" @@ -2568,7 +3802,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Afficher les boss de raid actuels", "PL": "TRANSLATE", "FI": "Listaa tämänhetkiset raidibossit", "ES": "Ver actuales jefes de incursión" @@ -2581,7 +3815,7 @@ "PT-BR": "Lista de todos os Pokémon.", "RU": "Список всех покемонов", "NO": "Liste over alle pokemon", - "FR": "Liste de tous les Pokémon", + "FR": "Liste de tous les Pokémon.", "PL": "Lista wszystkich Pokemonów.", "FI": "Listaa kaikki Pokemonit.", "ES": "Lista de todos los Pokémon." @@ -2594,7 +3828,7 @@ "PT-BR": "Erro! Nenhum pokemon encontrado!", "RU": "Ошибка! Покемон не найден!", "NO": "Feil: Ingen pokemon funnet!", - "FR": "Erreur, aucun Pokémon trouvé !", + "FR": "Erreur! Aucun Pokémon trouvé!", "PL": "Błąd! Pokemona nie znaleziono!", "FI": "Virhe! Pokemoneja ei löytynyt!", "ES": "¡Error! ¡No se encontró ningún Pokémon!" @@ -2607,7 +3841,7 @@ "PT-BR": "Selecione o chefe de raid para modificar:", "RU": "Выберите рейд босса для редактирования", "NO": "Velg raid boss som skal redigeres:", - "FR": "Sélectionne un boss de raid à modifier", + "FR": "Sélectionnez un boss de raid à modifier:", "PL": "Wybierz Bossa Rajdu żeby edytować:", "FI": "Valitse raidibossi muokattavaksi:", "ES": "Selecciona jefe de incursión para editar:" @@ -2620,7 +3854,7 @@ "PT-BR": "Nível da reide", "RU": "Уровень рейда", "NO": "Raid level", - "FR": "Niveau du raid", + "FR": "Niveau de raid", "PL": "Poziom Rajdu", "FI": "Raidin taso", "ES": "Nivel de incursión" @@ -2633,7 +3867,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Tous les niveaux de raid", "PL": "TRANSLATE", "FI": "Kaikki raiditasot", "ES": "Todos niveles de incursión" @@ -2659,7 +3893,7 @@ "PT-BR": "Nível atual da reide", "RU": "Текущий уровень рейда", "NO": "Nåværende raid level:", - "FR": "Niveau actuel du raid", + "FR": "Niveau actuel de raid:", "PL": "Aktualny poziom Rajdu:", "FI": "Nykyinen raiditaso:", "ES": "Nivel de incursión actual:" @@ -2672,7 +3906,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Nouveau statut", "PL": "TRANSLATE", "FI": "Uusi arvo", "ES": "Nuevo estado" @@ -2685,7 +3919,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Statut actuel:", "PL": "TRANSLATE", "FI": "Nykyinen arvo:", "ES": "Estado actual:" @@ -2698,7 +3932,7 @@ "PT-BR": "Selecione a opção para modificar:", "RU": "Выберите функцию для изменения:", "NO": "Velg egenskap som skal endres:", - "FR": "Sélectionne l'option à changer", + "FR": "Sélectionnez l'option à modifier:", "PL": "Wybierz aby edytować:", "FI": "Valitse muutettava ominaisuus:", "ES": "Selecciona la característica a cambiar:" @@ -2711,7 +3945,7 @@ "PT-BR": "PC:", "RU": "CP", "NO": "CP:", - "FR": "PC", + "FR": "PC:", "PL": "CP", "FI": "CP:", "ES": "PC:" @@ -2724,7 +3958,7 @@ "PT-BR": "Min PC", "RU": "Min CP", "NO": "Min CP", - "FR": "PC Min", + "FR": "PC min.", "PL": "Min CP", "FI": "Min CP", "ES": "Mín. PC" @@ -2737,7 +3971,7 @@ "PT-BR": "Max PC", "RU": "Max CP", "NO": "Maks CP", - "FR": "PC Max", + "FR": "PC max.", "PL": "Max CP", "FI": "Max CP", "ES": "Máx. PC" @@ -2750,7 +3984,7 @@ "PT-BR": "PC mín. potenciado", "RU": "Min boosted CP", "NO": "Min boostet CP", - "FR": "PC Min boosté", + "FR": "PC min. renforcés", "PL": "Min boosted CP", "FI": "Min buustattu CP", "ES": "Mín. PC potenciado" @@ -2763,7 +3997,7 @@ "PT-BR": "PC máx. potenciado", "RU": "Max boosted CP", "NO": "Maks boostet CP", - "FR": "PC Max boosté", + "FR": "PC max. renforcés", "PL": "Max Boosted CP", "FI": "Max buustattu CP", "ES": "Máx. PC potenciado" @@ -2776,7 +4010,7 @@ "PT-BR": "PC atual:", "RU": "Текущий CP:", "NO": "Nåværende CP:", - "FR": "PC actuel", + "FR": "PC actuels:", "PL": "Aktualne CP", "FI": "Nykyinen CP:", "ES": "PC actual:" @@ -2802,7 +4036,7 @@ "PT-BR": "Clima atual:", "RU": "Текущая погода:", "NO": "Værforhold:", - "FR": "Météo actuelle", + "FR": "Météo actuelle:", "PL": "Aktualna pogoda:", "FI": "Tämänhetkinen sää:", "ES": "Clima actual:" @@ -2815,7 +4049,7 @@ "PT-BR": "Novo clima:", "RU": "Новая погода:", "NO": "Været har endret seg:", - "FR": "Nouvelle météo", + "FR": "Nouvelle météo:", "PL": "Nowa pogoda:", "FI": "Uusi sää:", "ES": "Nuevo clima:" @@ -2828,7 +4062,7 @@ "PT-BR": "Quanto tempo a reide vai durar", "RU": "Как долго рейд будет идти?", "NO": "Hvor lenge varer raidet?", - "FR": "Dans combien de temps termine le raid ?", + "FR": "Dans combien de temps se termine le raid?", "PL": "Jak długo Rajd będzie trwać?", "FI": "Kauanko raidi kestää?", "ES": "¿Cuánto durará la incursión?" @@ -2848,13 +4082,13 @@ }, "select_gym_name": { "NL": "Selecteer Gym:", - "DE": "Bitte Gym auswählen:", + "DE": "Bitte Arena auswählen:", "EN": "Please select Gym:", "IT": "Seleziona Palestra:", "PT-BR": "Escolha o ginásio:", "RU": "Выберите Гим:", "NO": "Velg Gym:", - "FR": "Sélectionne l'arène", + "FR": "Sélectionnez l'arène:", "PL": "Wybierz Arene:", "FI": "Valitse sali:", "ES": "Por favor, selecciona el gimnasio:" @@ -2867,7 +4101,7 @@ "PT-BR": "Bora arrasar!", "RU": "А вот и мы!", "NO": "Kjør på!", - "FR": "C'est parti !", + "FR": "C'est parti!", "PL": "Zaczynamy!", "FI": "Nyt mennään!", "ES": "¡Aquí vamos!" @@ -2893,7 +4127,7 @@ "PT-BR": "Coordenadas enviadas com sucesso!", "RU": "Координаты успешно отправлены!", "NO": "Koordinater ble lagt til!", - "FR": "Coordonnées créés avec succès ", + "FR": "Coordonnées envoyées avec succès!", "PL": "Koordynaty dodano pomyślnie!", "FI": "Koordinaatit lähetetty onnistuneesti!", "ES": "¡Coordenadas enviadas con éxito!" @@ -2906,7 +4140,7 @@ "PT-BR": "Atualizar chefe da reide", "RU": "Обновить рейд босса", "NO": "Oppdater raid boss", - "FR": "Mise à jour boss de raid", + "FR": "Mettre à jour boss de raid", "PL": "Aktualizacja Bossa Rajdu", "FI": "Päivitä raidibossi", "ES": "Actualizar jefe de incursión" @@ -2919,7 +4153,7 @@ "PT-BR": "Atualizar Pokemon", "RU": "Обновить Покемона", "NO": "Oppdater Pokemon", - "FR": "Mise à jour Pokémon", + "FR": "Mettre à jour Pokémon", "PL": "Aktualizacja Pokemona", "FI": "Päivitä Pokemoni", "ES": "Actualizar Pokémon" @@ -2945,7 +4179,7 @@ "PT-BR": "Por favor, antes me envie a localização.", "RU": "Или отправить мне локацию", "NO": "Eller send meg lokasjonen din ved å trykke på bindersen", - "FR": "Ou envoi moi la localisation", + "FR": "Ou envoyez-moi la localisation.", "PL": "Albo wyślij mi swoje położenie", "FI": "Tai lähetä minulle sijainti.", "ES": "O envíame una ubicación." @@ -2958,7 +4192,7 @@ "PT-BR": "Ou escolha uma raid por ginásio:", "RU": "Сделать рейд, выбрав гим:", "NO": "Opprett raid ved å velge fra gym listen", - "FR": "Faire une sélection de raid par arène", + "FR": "Créer un raid en sélectionnant une arène:", "PL": "Wygeneruj nowy Rajd, wybierając Arene:", "FI": "Luo raidi salivalinnan mukaan:", "ES": "Crear incursión con selección de gimnasio:" @@ -3010,7 +4244,7 @@ "PT-BR": "Time do ginásio escolhido para", "RU": "Команду гима:", "NO": "Gym Team endret til:", - "FR": "Couleur d'arène réglée sur", + "FR": "Équipe d'arène réglée sur:", "PL": "Kolor Areny zmieniony na:", "FI": "Salin joukkueeksi asetettu:", "ES": "Equipo de gimnasio puesto en:" @@ -3023,7 +4257,7 @@ "PT-BR": "Nome de time inválido - escreva: Mystic, Valor, Instinct ou Blue, Red, Yellow", "RU": "Неверное имя команд - напишите: Mystic, Valor, Instinct или Синий, Красный, Желтый", "NO": "Ugyldig team navn - skriv: Mystic, Valor, Instinct or Blue, Red, Yellow", - "FR": "Couleur invalide - écrit : Sagesse, Bravoure, Intuition ou Bleu, Rouge, Jaune", + "FR": "Équipe invalide - écrivez : Sagesse, Bravoure, Intuition ou Bleu, Rouge, Jaune ", "PL": "Zła nazwa Teamu - napisz: niebieski, czerwony albo żółty", "FI": "Virheellinen joukkueen nimi - kirjoita: Mystic, Valor, Instinct tai Blue, Red, Yellow", "ES": "Nombre de equipo invalido - escribe: Sabiduría, Valor, Instinto o Azul, Rojo, Amarillo" @@ -3036,7 +4270,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Utilisez les commandes suivantes pour modifier davantage l'arène:", "PL": "TRANSLATE", "FI": "Käytä seuraavia komentoja muokataksesi salia enemmän:", "ES": "Utiliza los siguientes comandos para editar más el gimnasio:" @@ -3049,49 +4283,36 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Pour obtenir l'identifiant de l'arène et plus de détails, utilisez la commande /gym.", "PL": "TRANSLATE", "FI": "Saataksesi salin id numeron tai muita tietoja, käytä /gym -komentoa.", "ES": "Para obtener la identificación y más detalles del gimnasio, usa el comando /gym." }, - "gym_name_instructions": { - "NL": "Om de gym naam aan te passen stuur /gymname gym id, nieuwe gym naam!", - "DE": "Zum Ändern eines Arena-Namen bitte /gymname Arena-ID, neuer Arena-Name eingeben!", - "EN": "To change the name of a gym please enter /gymname gym id, new gym name!", - "IT": "TRANSLATE", - "PT-BR": "TRANSLATE", - "RU": "TRANSLATE", - "NO": "TRANSLATE", - "FR": "TRANSLATE", - "PL": "TRANSLATE", - "FI": "Vaihtaaksesi salin nimeä komenna /gymname salin id, uusi salin nimi!", - "ES": "Para cambiar el nombre de un gimnasio, escribe /gymname gym id, nuevo nombre de gimnasio." - }, "gym_name_example": { - "NL": "Als voorbeeld: /gymname 34, Markt 61, 3131 CR Vlaardingen ", - "DE": "Zum Beispiel: /gymname 34, Wasserfall im Park", - "EN": "For example: /gymname 34, Waterfall in the park!", + "NL": "Als voorbeeld: /gymname Markt 61, 3131 CR Vlaardingen ", + "DE": "Zum Beispiel: /gymname Wasserfall im Park", + "EN": "For example: /gymname Waterfall in the park!", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Par exemple: /gymname Fontaine du parc!", "PL": "TRANSLATE", - "FI": "Esimerkiksi: /gymname 34, Vapauden Muistomerkki!", - "ES": "Por ejemplo: /gymname 34, Waterfall in the park" + "FI": "Esimerkiksi: /gymname Vapauden Muistomerkki!", + "ES": "Por ejemplo: /gymname Waterfall in the park" }, "gym_id_name_missing": { - "NL": "Error! Gym id of naam mist!", - "DE": "Fehler! Arena-ID oder Name fehlt!", - "EN": "Error! Gym id or name is missing!", + "NL": "Error! Gym naam mist!", + "DE": "Fehler! Arena-Name fehlt!", + "EN": "Error! Gym name is missing!", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Erreur! Le nom de l'arène est manquant!", "PL": "TRANSLATE", - "FI": "Virhe! Salin id numero tai nimi puuttuu!", - "ES": "¡Error! ¡Faltan gym id o nombre!" + "FI": "Virhe! Salin nimi puuttuu!", + "ES": "TRANSLATE" }, "gym_name_updated": { "NL": "Gym naam aangepast.", @@ -3101,11 +4322,37 @@ "PT-BR": "Nome do ginásio atualizado.", "RU": "Название гима обновлено.", "NO": "Gym navn oppdatert.", - "FR": "Nom de l'arène mis à jour", + "FR": "Nom de l'arène mis à jour.", "PL": "Nazwa Areny zaktualizowana", "FI": "Salin nimi päivitetty.", "ES": "Nombre del gimnasio actualizado." }, + "gym_name_edit": { + "NL": "Gym naam aanpassen", + "DE": "Arena-Name bearbeiten", + "EN": "Edit gym name", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Modifier le nom de l'arène", + "PL": "TRANSLATE", + "FI": "Muokkaa salin nimeä", + "ES": "TRANSLATE" + }, + "gym_name_instructions": { + "NL": "Stuur me de gym naam:", + "DE": "Arena-Name eingeben:", + "EN": "Send me the gym name:", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Envoyez-moi le nom de l'arène:", + "PL": "TRANSLATE", + "FI": "Lähetä minulle salin nimi:", + "ES": "TRANSLATE" + }, "overview_share": { "NL": "Overzicht delen", "DE": "Übersicht teilen", @@ -3140,7 +4387,7 @@ "PT-BR": "Mostrar raids ativas como lista", "RU": "Показать активные рейды списком", "NO": "Vis aktive raid som liste", - "FR": "Voir les raids en cours", + "FR": "Afficher les raids en cours sous forme de liste", "PL": "Pokaż liste aktywnych rajdów", "FI": "Näytä aktiiviset raidit listana", "ES": "Mostrar incursiones activas como lista" @@ -3153,7 +4400,7 @@ "PT-BR": "Compartilhar / deletar detalhes de raid por chat", "RU": "поделиться / удалить обзор рейда через чат", "NO": "Del / slett raid oversikten per chat", - "FR": "Partager / supprimer l'aperçu des raids par conversation", + "FR": "Partager / supprimer l'aperçu de raid par conversation", "PL": "Udostępnij / usuń liste przez czat", "FI": "Jaa / poista raidiyleiskatsaus per chatti", "ES": "Compartir / eliminar la descripción general de la incursión por chat" @@ -3192,7 +4439,7 @@ "PT-BR": "Deletar detalhe de raid para", "RU": "Удалить обзор рейда для", "NO": "Slett raid oversikt for", - "FR": "Supprimer l'aperçu du raid pour", + "FR": "Supprimer l'aperçu de raid pour", "PL": "Usuń podgląd Rajdu dla", "FI": "Poista raidiyleiskatsaus chatista", "ES": "Eliminar descripción general de incursión para" @@ -3205,7 +4452,7 @@ "PT-BR": "Nenhum detalhe de raid encontrado no sistema!", "RU": "В системе нет обзора рейдов!", "NO": "Ingen oppsummering funnet i systemet!", - "FR": "Aucun aperçu de raid trouvé dans le système", + "FR": "Aucun aperçu de raid trouvé dans le système!", "PL": "Brak podglądów Rajdów w systemie!", "FI": "Järjestelmästä ei löytynyt raidi yleiskatsauksia!", "ES": "¡No se han encontrado descripciones generales en el sistema!" @@ -3218,7 +4465,7 @@ "PT-BR": "Detalhe de raid deletado com sucesso!", "RU": "Обзор рейдов успешно удален!", "NO": "Raid oppsummeringen ble slettet!", - "FR": "L'aperçu de raid a été supprimé", + "FR": "L'aperçu de raid a été supprimé!", "PL": "Podgląd Rajdu usunięty!", "FI": "Raidi yleiskatsaus poistettu onnistuneesti!", "ES": "¡Descripción general eliminada con éxito!" @@ -3231,7 +4478,7 @@ "PT-BR": "O deletamento dos detalhes da raid foi cancelado!", "RU": "Удаление обзора рейда было отменено!", "NO": "Sletting av raid oppsummeringen ble avbrutt!", - "FR": "La suppression de l'aperçu du raid a été annulée", + "FR": "La suppression de l'aperçu de raid a été annulée!", "PL": "Usuwanie podglądu anulowane!", "FI": "Raidi yleiskatsauksen poisto peruutettiin!", "ES": "¡Eliminación de la descripción general de la incursión cancelada!" @@ -3257,7 +4504,7 @@ "PT-BR": "Realmente deletar esta raid?", "RU": "Действительно удалить этот рейд?", "NO": "Skal du virkelig slette raidet?", - "FR": "Supprimer ce raid ?", + "FR": "Vraiment supprimer ce raid?", "PL": "Na pewno usunąć ten Rajd?", "FI": "Varmasti poista tämä raidi?", "ES": "¿De verdad eliminar esta incursión?" @@ -3270,7 +4517,7 @@ "PT-BR": "O deletamento da raid foi cancelado!", "RU": "Удаление рейда было отменено!", "NO": "Sletting av raidet ble avbrutt!", - "FR": "La suppression du raid a été annulée", + "FR": "La suppression du raid a été annulée!", "PL": "Usuwanie Rajdu anulowane!", "FI": "Raidin poisto peruutettiin!", "ES": "¡Eliminación de la incursión fue cancelada!" @@ -3283,7 +4530,7 @@ "PT-BR": "Raid deletado com sucesso!", "RU": "Рейд был успешно удален!", "NO": "Raidet ble slettet", - "FR": "Le raid a été annulé", + "FR": "Le raid a été supprimé avec succès!", "PL": "Rajd usunięty!", "FI": "Raidi poistettiin onnistuneesti!", "ES": "¡Incursión eliminada con éxito!" @@ -3296,7 +4543,7 @@ "PT-BR": "A qualquer momento", "RU": "В любое время", "NO": "Alle tider passer", - "FR": "A tout moment", + "FR": "À tout moment", "PL": "W każdej chwili", "FI": "Milloin tahansa", "ES": "Cualquiera hora" @@ -3322,7 +4569,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Notifications de raid désactivées!", "PL": "TRANSLATE", "FI": "Raidihäly pois käytöstä!", "ES": "¡Alertas de incursión desactivadas!" @@ -3335,33 +4582,33 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Notifications de raid activées!", "PL": "TRANSLATE", "FI": "Raidihäly käytössä!", "ES": "¡Alertas de incursión activadas!" }, "switch_alarm_on": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", + "NL": "Zet automatische raid alarm aan", + "DE": "Automatische Raid-Alarme aktivieren", "EN": "Enable automatic raid alerts", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Activer les notifications de raid automatiques", "PL": "TRANSLATE", "FI": "Automaattinen raidihäly päälle", "ES": "Activar alertas automáticas de incursiones" }, "switch_alarm_off": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", + "NL": "Zet automatisch raid alarm uit", + "DE": "Automatische Raid-Alarme deaktivieren", "EN": "Disable automatic raid alerts", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Désactiver les notifications de raid automatiques", "PL": "TRANSLATE", "FI": "Automaattinen raidihäly pois päältä", "ES": "Desactivar alertas automáticas de incursiones" @@ -3374,20 +4621,20 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Dresseur supplémentaire!", "PL": "TRANSLATE", "FI": "Uusi osallistuja!", "ES": "¡Entrenador adicional!" }, "alert_add_alien_trainer": { "NL": "Een gebruiker is uitgenodigd buiten deze groep!", - "DE": "TRANSLATE", + "DE": "Trainer lädt Spieler außerhalb der Gruppe ein!", "EN": "User is inviting a player from outside the group!", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "L'utilisateur invite un dresseur en dehors du groupe!", "PL": "TRANSLATE", "FI": "Pelaaja kutsuu kaverin ryhmän ulkopuolelta!", "ES": "¡Usuario está invitando a un entrenador fuera del grupo!" @@ -3400,7 +4647,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Dresseur en retard!", "PL": "TRANSLATE", "FI": "Myöhästyvä osallistuja!", "ES": "¡Entrenador tarda!" @@ -3413,7 +4660,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Annulation!", "PL": "TRANSLATE", "FI": "Peruutus!", "ES": "¡Cancelación!" @@ -3426,7 +4673,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Vient pour", "PL": "TRANSLATE", "FI": "Ilmoittautuneet,", "ES": "Viene para" @@ -3439,7 +4686,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Ne vient PAS pour", "PL": "TRANSLATE", "FI": "Peruuttaneet,", "ES": "NO viene para" @@ -3447,14 +4694,14 @@ "alert_every_poke": { "NL": "Komt voor", "DE": "Kommt für jeden Raid-Boss", - "EN": "Coming for", + "EN": "Coming for any boss", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Vient pour tout boss de raid", "PL": "TRANSLATE", - "FI": "Ilmoittautuneet,", + "FI": "Osallistuu mihin tahansa bossiin", "ES": "Viene para" }, "alert_new_att": { @@ -3465,7 +4712,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Nouvelle participation!", "PL": "TRANSLATE", "FI": "Uusi osallistuja!", "ES": "¡Nueva participación!" @@ -3478,7 +4725,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Participation à distance!", "PL": "TRANSLATE", "FI": "Etäosallistuminen!", "ES": "¡Participación remota!" @@ -3491,7 +4738,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Pas plus de participants à distance!", "PL": "TRANSLATE", "FI": "Ei enää etäosallistujia!", "ES": "¡No más participación remota!" @@ -3504,7 +4751,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Le raid commence maintenant!", "PL": "TRANSLATE", "FI": "Raidi alkaa nyt!", "ES": "¡La incursión empieza ahora!" @@ -3517,7 +4764,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Voici comment participer au combat:", "PL": "TRANSLATE", "FI": "Näin pääset taisteluun:", "ES": "Cómo entrar en la batalla:" @@ -3530,7 +4777,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Groupe de raid privé!", "PL": "TRANSLATE", "FI": "Yksityinen raidiryhmä!", "ES": "¡Grupo de incursión privado!" @@ -3543,7 +4790,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Groupe de raid publique!", "PL": "TRANSLATE", "FI": "Julkinen raidiryhmä!", "ES": "¡Grupo de incursión público!" @@ -3556,7 +4803,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Nouvelle heure de participation!", "PL": "TRANSLATE", "FI": "Uusi aika!", "ES": "¡Nueva hora de participación!" @@ -3569,7 +4816,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Boss de raid mis à jour!", "PL": "TRANSLATE", "FI": "Raidibossi päivitetty!", "ES": "¡Jefe de incursión actualizado!" @@ -3582,59 +4829,59 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Pas plus de dresseurs supplémentaires!", "PL": "TRANSLATE", "FI": "Ei ylimääräisiä kouluttajia!", "ES": "¡Sin entrenadores adicionales!" }, "alert_want_invite": { "NL": "Wenst uitgenodigd te worden.", - "DE": "TRANSLATE", + "DE": "Möchte eingeladen werden.", "EN": "Wishes to be invited.", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Souhaite être invité.", "PL": "TRANSLATE", "FI": "Toivoo tulevansa kutsutuksi.", "ES": "Desea ser invitado." }, "alert_no_want_invite": { "NL": "Wil niet langer worden uitgenodigd.", - "DE": "TRANSLATE", + "DE": "Möchte nicht länger eingeladen werden.", "EN": "No longer wishes to be invited.", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Ne souhaite plus être invité.", "PL": "TRANSLATE", "FI": "Ei enää toivo kutsua.", "ES": "Ya no desea ser invitado." }, "alert_can_invite": { "NL": "Doet niet mee, maar kan wel invites versturen.", - "DE": "TRANSLATE", + "DE": "Spielt selbst nicht mit, kann aber einladen.", "EN": "Is not attending but can send invites.", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Ne participe pas mais peut envoyer des invitations.", "PL": "TRANSLATE", "FI": "Ei osallistu raidiin, mutta voi lähettää kutsuja.", "ES": "No asiste pero puede enviar invitaciones." }, "alert_no_can_invite": { "NL": "Kan niet langer andere uitnodigingen versturen.", - "DE": "TRANSLATE", + "DE": "Kann nicht länger andere einladen.", "EN": "Is no longer able to invite others.", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Ne peut plus envoyer des invitations.", "PL": "TRANSLATE", "FI": "Ei enää pysty kutsumaan muita.", "ES": "Ya no puede invitar a otros." @@ -3647,46 +4894,46 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Événement", "PL": "TRANSLATE", "FI": "Tapahtuma", "ES": "Evento" }, "event_note": { "NL": "Event info", - "DE": "Event Notiz", + "DE": "Event-Notiz", "EN": "Event note", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Notice d'événement", "PL": "TRANSLATE", "FI": "Tapahtuman lisätiedot", "ES": "Nota de evento" }, "event_note_add": { "NL": "Voeg event infot toe", - "DE": "Event Notiz hinzufügen", + "DE": "Event-Notiz hinzufügen", "EN": "Add event note", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Ajouter notice d'événement", "PL": "TRANSLATE", "FI": "Lisää raiditapahtumaan lisätietoja", "ES": "Añadir nota de evento" }, "event_note_edit": { "NL": "Wijzig de event info", - "DE": "Event Notiz ändern", + "DE": "Event-Notiz bearbeiten", "EN": "Edit event note", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Modifier notice d'événement", "PL": "TRANSLATE", "FI": "Muokkaa raiditapahtuman lisätietoja", "ES": "Editar nota de evento" @@ -3699,11 +4946,154 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Ajouter plus d'informations pour cette annonce d'événement. (par exemple: itinéraire)", "PL": "TRANSLATE", "FI": "Lisää ilmoituskohtaisia lisätietoja tapahtumaan liittyen. (esim. reitti)", "ES": "Agrega más información para este anuncio específico sobre el evento. (por ejemplo: ruta)" }, + "events_manage": { + "NL": "Event manager", + "DE": "Event verwalten", + "EN": "Manage events", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Gérer les événements", + "PL": "TRANSLATE", + "FI": "Hallitse tapahtumia", + "ES": "TRANSLATE" + }, + "events_create": { + "NL": "Maak een nieuw event", + "DE": "Neues Event erstellen", + "EN": "Create a new event", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Créer un nouvel événement", + "PL": "TRANSLATE", + "FI": "Luo uusi tapahtuma", + "ES": "TRANSLATE" + }, + "events_created": { + "NL": "Event succesvol aangemaakt!", + "DE": "Event erfolgreich erstellt!", + "EN": "Event created succesfully!", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Événement créé avec succès!", + "PL": "TRANSLATE", + "FI": "Tapahtuma luotu onnistuneesti!", + "ES": "TRANSLATE" + }, + "events_give_name": { + "NL": "Geef het event een naam", + "DE": "Event-Name eingeben", + "EN": "Give the event a name", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Nommer l'événement", + "PL": "TRANSLATE", + "FI": "Anna tapahtuman nimi", + "ES": "TRANSLATE" + }, + "events_give_description": { + "NL": "Geef de omschrijving van het event", + "DE": "Event-Beschreibung eingeben", + "EN": "Give the event a description", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Décrire l'événement", + "PL": "TRANSLATE", + "FI": "Anna tapahtuman lisätiedot", + "ES": "TRANSLATE" + }, + "events_no_description": { + "NL": "Geen omschrijving", + "DE": "Keine Beschreibung", + "EN": "No description", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Aucune description", + "PL": "TRANSLATE", + "FI": "Ei kuvausta", + "ES": "TRANSLATE" + }, + "events_edit_name": { + "NL": "Wijzig event naam", + "DE": "Event-Name bearbeiten", + "EN": "Edit event name", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Modifier le nom de l'événement", + "PL": "TRANSLATE", + "FI": "Muokkaa tapahtuman nimeä", + "ES": "TRANSLATE" + }, + "events_edit_description": { + "NL": "Wijzig de event omschrijving", + "DE": "Event-Beschreibung bearbeiten", + "EN": "Edit event description", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Modifier la description de l'événement", + "PL": "TRANSLATE", + "FI": "Muokkaa tapahtuman kuvausta", + "ES": "TRANSLATE" + }, + "events_edit_raid_poll": { + "NL": "Wijzig event raid poll", + "DE": "Event-Raid-Abstimmung bearbeiten", + "EN": "Edit event raid poll", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Modifier l'annonce de l'événement de raid", + "PL": "TRANSLATE", + "FI": "Muokkaa tapahtuman raidi-ilmoitusta", + "ES": "TRANSLATE" + }, + "events_delete": { + "NL": "Verwijder event", + "DE": "Event löschen", + "EN": "Delete event", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Supprimer l'événement", + "PL": "TRANSLATE", + "FI": "Poista tapahtuma", + "ES": "TRANSLATE" + }, + "events_delete_confirmation": { + "NL": "Wil je dit event echt verwijderen?", + "DE": "Dieses Event wirklich löschen?", + "EN": "Do you really want to delete this event?", + "IT": "TRANSLATE", + "PT-BR": "TRANSLATE", + "RU": "TRANSLATE", + "NO": "TRANSLATE", + "FR": "Vraiment supprimer cet événement?", + "PL": "TRANSLATE", + "FI": "Haluatko varmasti poistaa tämän tapahtuman?", + "ES": "TRANSLATE" + }, "Participate": { "NL": "Deelnemer", "DE": "Teilnehmen", @@ -3712,7 +5102,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Participer", "PL": "TRANSLATE", "FI": "Osallistu", "ES": "Participar" @@ -3725,7 +5115,7 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Participe", "PL": "TRANSLATE", "FI": "Osallistuu", "ES": "Participando" @@ -3738,87 +5128,87 @@ "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Vous n'avez pas encore enregistré vos informations de dresseur. Voulez-vous le faire maintenant?", "PL": "TRANSLATE", "FI": "Et ole vielä tallentanut tietoja pelihahmostasi. Haluaisitko tehdä sen nyt?", "ES": "Aún no has guardado la información de tu entrenador. ¿Te gustaría hacerlo ahora?" }, "history_title": { "NL": "Zie raid poll geschiedenis", - "DE": "TRANSLATE", + "DE": "Raid-Historie ansehen", "EN": "View raid poll history.", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Afficher l'historique des annonces de raid.", "PL": "TRANSLATE", "FI": "Katsele vanhoja raidi-ilmoituksia.", "ES": "Ver historial de anuncios de incursiones." }, "history_displaying_month": { "NL": "Momenteel worden er raids weergegeven voor", - "DE": "TRANSLATE", + "DE": "Zeigt derzeit Raids an für", "EN": "Currently displaying raids for", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Affichage en cours des raids pour", "PL": "TRANSLATE", "FI": "Tällä hetkellä näytetään", "ES": "Actualmente se muestran incursiones para" }, "history_select_raid": { "NL": "Selecteer raid", - "DE": "TRANSLATE", + "DE": "Raid auswählen", "EN": "Select raid", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Choisir un raid", "PL": "TRANSLATE", "FI": "Valitse raidi", "ES": "Selecciona incursión" }, "history_no_raids_found": { "NL": "Geen raids met spelers gevonden!", - "DE": "TRANSLATE", + "DE": "Keine Raids mit Teilnehmern gefunden!", "EN": "No raids with attendees found!", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Aucun raid avec participants trouvé!", "PL": "TRANSLATE", "FI": "Yhtään raidia ei löytynyt jossa olisi osallistujia!", "ES": "¡No se encontraron incursiones con los asistentes!" }, "date": { - "NL": "TRANSLATE", + "NL": "Datum", "DE": "Datum", "EN": "Date", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Date", "PL": "TRANSLATE", "FI": "Päivämäärä", "ES": "Fecha" }, "current_gymarea": { - "NL": "TRANSLATE", - "DE": "TRANSLATE", + "NL": "Gym gebied", + "DE": "Aktuelles Arena-Gebiet", "EN": "Current gymarea", "IT": "TRANSLATE", "PT-BR": "TRANSLATE", "RU": "TRANSLATE", "NO": "TRANSLATE", - "FR": "TRANSLATE", + "FR": "Zone actuelle", "PL": "TRANSLATE", "FI": "Salit alueelta", "ES": "Zona de gimnasio actual" } -} \ No newline at end of file +} diff --git a/core/lang/pokemon.json b/lang/pokemon.json similarity index 83% rename from core/lang/pokemon.json rename to lang/pokemon.json index b1470fd8..e524d521 100644 --- a/core/lang/pokemon.json +++ b/lang/pokemon.json @@ -138,7 +138,7 @@ }, "pokemon_id_24": { "EN": "Arbok", - "RU": "Арбок" + "RU": "Эрбок" }, "pokemon_id_25": { "EN": "Pikachu", @@ -199,7 +199,7 @@ "pokemon_id_37": { "EN": "Vulpix", "FR": "Goupix", - "RU": "Валпикс" + "RU": "Вульпикс" }, "pokemon_id_38": { "EN": "Ninetales", @@ -402,7 +402,7 @@ "pokemon_id_73": { "EN": "Tentacruel", "DE": "Tentoxa", - "RU": "Тентакрул" + "RU": "Тентакруэль" }, "pokemon_id_74": { "EN": "Geodude", @@ -414,7 +414,7 @@ "EN": "Graveler", "FR": "Gravalanch", "DE": "Georok", - "RU": "Гравелер" + "RU": "Грейвлер" }, "pokemon_id_76": { "EN": "Golem", @@ -461,7 +461,7 @@ "FR": "Canarticho", "DE": "Porenta", "IT": "Farfetch’d", - "RU": "Фарфетчд" + "RU": "Фарфетч'т" }, "pokemon_id_84": { "EN": "Doduo", @@ -483,7 +483,7 @@ "EN": "Dewgong", "FR": "Lamantine", "DE": "Jugong", - "RU": "Дьюгонг" + "RU": "Дюгонг" }, "pokemon_id_88": { "EN": "Grimer", @@ -534,7 +534,7 @@ "EN": "Drowzee", "FR": "Soporifik", "DE": "Traumato", - "RU": "Драузи" + "RU": "Дроузи" }, "pokemon_id_97": { "EN": "Hypno", @@ -554,7 +554,7 @@ "EN": "Voltorb", "FR": "Voltorbe", "DE": "Voltobal", - "RU": "Волторб" + "RU": "Вальторб" }, "pokemon_id_101": { "EN": "Electrode", @@ -566,7 +566,7 @@ "EN": "Exeggcute", "FR": "Noeunoeuf", "DE": "Owei", - "RU": "Эксегкьют" + "RU": "Экзегкьют" }, "pokemon_id_103": { "EN": "Exeggutor", @@ -602,7 +602,7 @@ "EN": "Lickitung", "FR": "Excelangue", "DE": "Schlurp", - "RU": "Ликитанг" + "RU": "Ликитунг" }, "pokemon_id_109": { "EN": "Koffing", @@ -727,7 +727,7 @@ "EN": "Gyarados", "FR": "Léviator", "DE": "Garados", - "RU": "Гиарадос" + "RU": "Гиардос" }, "pokemon_id_131": { "EN": "Lapras", @@ -874,7 +874,7 @@ "pokemon_id_157": { "EN": "Typhlosion", "DE": "Tornupto", - "RU": "Тайфложн" + "RU": "Тифложн" }, "pokemon_id_158": { "EN": "Totodile", @@ -892,7 +892,7 @@ "EN": "Feraligatr", "FR": "Aligatueur", "DE": "Impergator", - "RU": "Фералигейтор" + "RU": "Фералигатор" }, "pokemon_id_161": { "EN": "Sentret", @@ -971,11 +971,11 @@ }, "pokemon_id_175": { "EN": "Togepi", - "RU": "Тогэпи" + "RU": "Тогепи" }, "pokemon_id_176": { "EN": "Togetic", - "RU": "Тогэтик" + "RU": "Тогетик" }, "pokemon_id_177": { "EN": "Natu", @@ -983,7 +983,7 @@ }, "pokemon_id_178": { "EN": "Xatu", - "RU": "Ксату" + "RU": "Зату" }, "pokemon_id_179": { "EN": "Mareep", @@ -1010,7 +1010,7 @@ }, "pokemon_id_183": { "EN": "Marill", - "RU": "Мэрилл" + "RU": "Марилл" }, "pokemon_id_184": { "EN": "Azumarill", @@ -1130,7 +1130,7 @@ "EN": "Pineco", "FR": "Pomdepik", "DE": "Tannza", - "RU": "Пайнеко" + "RU": "Пинеко" }, "pokemon_id_205": { "EN": "Forretress", @@ -1148,7 +1148,7 @@ "EN": "Gligar", "FR": "Scorplane", "DE": "Skorgla", - "RU": "Глайгар" + "RU": "Глигар" }, "pokemon_id_208": { "EN": "Steelix", @@ -1161,7 +1161,7 @@ }, "pokemon_id_210": { "EN": "Granbull", - "RU": "Грэнбулл" + "RU": "Гранбулл" }, "pokemon_id_211": { "EN": "Qwilfish", @@ -1190,7 +1190,7 @@ "EN": "Sneasel", "FR": "Farfuret", "DE": "Sniebel", - "RU": "Снизел" + "RU": "Снизель" }, "pokemon_id_216": { "EN": "Teddiursa", @@ -1276,15 +1276,15 @@ }, "pokemon_id_231": { "EN": "Phanpy", - "RU": "Фэнпи" + "RU": "Фанпи" }, "pokemon_id_232": { "EN": "Donphan", - "RU": "Донфэн" + "RU": "Донфан" }, "pokemon_id_233": { "EN": "Porygon2", - "RU": "Поригон-2" + "RU": "Поригон2" }, "pokemon_id_234": { "EN": "Stantler", @@ -1302,7 +1302,7 @@ "EN": "Tyrogue", "FR": "Debugant", "DE": "Rabauz", - "RU": "Тайрог" + "RU": "Тироуг" }, "pokemon_id_237": { "EN": "Hitmontop", @@ -1346,7 +1346,7 @@ }, "pokemon_id_245": { "EN": "Suicune", - "RU": "Суикун" + "RU": "Суйкун" }, "pokemon_id_246": { "EN": "Larvitar", @@ -1356,7 +1356,7 @@ "pokemon_id_247": { "EN": "Pupitar", "FR": "Ymphect", - "RU": "Пьюпитар" + "RU": "Пупитар" }, "pokemon_id_248": { "EN": "Tyranitar", @@ -1452,7 +1452,7 @@ "EN": "Linoone", "FR": "Linéon", "DE": "Geradaks", - "RU": "Лайнун" + "RU": "Линун" }, "pokemon_id_265": { "EN": "Wurmple", @@ -1533,7 +1533,7 @@ "pokemon_id_278": { "EN": "Wingull", "FR": "Goélise", - "RU": "Вингалл" + "RU": "Вингулл" }, "pokemon_id_279": { "EN": "Pelipper", @@ -1544,7 +1544,7 @@ "EN": "Ralts", "FR": "Tarsal", "DE": "Trasla", - "RU": "Ролтс" + "RU": "Ральтс" }, "pokemon_id_281": { "EN": "Kirlia", @@ -1553,7 +1553,7 @@ "pokemon_id_282": { "EN": "Gardevoir", "DE": "Guardevoir", - "RU": "Гардевойр" + "RU": "Гардевуар" }, "pokemon_id_283": { "EN": "Surskit", @@ -1583,7 +1583,7 @@ "EN": "Slakoth", "FR": "Parecool", "DE": "Bummelz", - "RU": "Слакот" + "RU": "Слякот" }, "pokemon_id_288": { "EN": "Vigoroth", @@ -1645,7 +1645,7 @@ "EN": "Nosepass", "FR": "Tarinor", "DE": "Nasgnet", - "RU": "Ноузпасс" + "RU": "Ноуспасс" }, "pokemon_id_300": { "EN": "Skitty", @@ -1655,13 +1655,13 @@ "pokemon_id_301": { "EN": "Delcatty", "DE": "Enekoro", - "RU": "Делкатти" + "RU": "Деликэтти" }, "pokemon_id_302": { "EN": "Sableye", "FR": "Ténéfix", "DE": "Zobiris", - "RU": "Сэйблай" + "RU": "Сабляй" }, "pokemon_id_303": { "EN": "Mawile", @@ -1714,17 +1714,17 @@ "pokemon_id_311": { "EN": "Plusle", "FR": "Posipi", - "RU": "Пласл" + "RU": "Плюсл" }, "pokemon_id_312": { "EN": "Minun", "FR": "Négapi", - "RU": "Майнун" + "RU": "Минун" }, "pokemon_id_313": { "EN": "Volbeat", "FR": "Muciole", - "RU": "Волбит" + "RU": "Вольбит" }, "pokemon_id_314": { "EN": "Illumise", @@ -1740,7 +1740,7 @@ "EN": "Gulpin", "FR": "Gloupti", "DE": "Schluppuck", - "RU": "Гальпин" + "RU": "Гульпин" }, "pokemon_id_317": { "EN": "Swalot", @@ -1760,28 +1760,28 @@ }, "pokemon_id_320": { "EN": "Wailmer", - "RU": "Вэйлмер" + "RU": "Вейлмер" }, "pokemon_id_321": { "EN": "Wailord", - "RU": "Вэйлорд" + "RU": "Вейлорд" }, "pokemon_id_322": { "EN": "Numel", "FR": "Chamallot", "DE": "Camaub", - "RU": "Намел" + "RU": "Нумел" }, "pokemon_id_323": { "EN": "Camerupt", "FR": "Camérupt", - "RU": "Камерапт" + "RU": "Камерупт" }, "pokemon_id_324": { "EN": "Torkoal", "FR": "Chartor", "DE": "Qurtel", - "RU": "Торкол" + "RU": "Торкоал" }, "pokemon_id_325": { "EN": "Spoink", @@ -1818,7 +1818,7 @@ "pokemon_id_331": { "EN": "Cacnea", "DE": "Tuska", - "RU": "Какнея" + "RU": "Какния" }, "pokemon_id_332": { "EN": "Cacturne", @@ -1829,11 +1829,11 @@ "EN": "Swablu", "FR": "Tylton", "DE": "Wablu", - "RU": "Сваблу" + "RU": "Сваблю" }, "pokemon_id_334": { "EN": "Altaria", - "RU": "Алтария" + "RU": "Альтария" }, "pokemon_id_335": { "EN": "Zangoose", @@ -1863,7 +1863,7 @@ "EN": "Barboach", "FR": "Barloche", "DE": "Schmerbe", - "RU": "Барбоч" + "RU": "Барбоуч" }, "pokemon_id_340": { "EN": "Whiscash", @@ -1881,7 +1881,7 @@ "EN": "Crawdaunt", "FR": "Colhomard", "DE": "Krebutack", - "RU": "Крадонт" + "RU": "Краудонт" }, "pokemon_id_343": { "EN": "Baltoy", @@ -1924,7 +1924,7 @@ "pokemon_id_350": { "EN": "Milotic", "FR": "Milobellus", - "RU": "Майлотик" + "RU": "Милотик" }, "pokemon_id_351": { "EN": "Castform", @@ -1966,7 +1966,7 @@ "EN": "Chimecho", "FR": "Éoko", "DE": "Palimpalim", - "RU": "Чаймеко" + "RU": "Чимечо" }, "pokemon_id_359": { "EN": "Absol", @@ -2040,7 +2040,7 @@ "EN": "Bagon", "FR": "Draby", "DE": "Kindwurm", - "RU": "Бейгон" + "RU": "Багон" }, "pokemon_id_372": { "EN": "Shelgon", @@ -2058,7 +2058,7 @@ "EN": "Beldum", "FR": "Terhal", "DE": "Tanhel", - "RU": "Белдам" + "RU": "Белдум" }, "pokemon_id_375": { "EN": "Metang", @@ -2216,7 +2216,7 @@ "pokemon_id_405": { "EN": "Luxray", "DE": "Luxtra", - "RU": "Лаксрэй" + "RU": "Лаксрей" }, "pokemon_id_406": { "EN": "Budew", @@ -2226,7 +2226,7 @@ }, "pokemon_id_407": { "EN": "Roserade", - "RU": "Розрейд" + "RU": "Розерейд" }, "pokemon_id_408": { "EN": "Cranidos", @@ -2238,7 +2238,7 @@ "EN": "Rampardos", "FR": "Charkos", "DE": "Rameidon", - "RU": "Рэмпардос" + "RU": "Рампардос" }, "pokemon_id_410": { "EN": "Shieldon", @@ -2288,7 +2288,7 @@ "EN": "Buizel", "FR": "Mustébouée", "DE": "Bamelin", - "RU": "Буизел" + "RU": "Буизель" }, "pokemon_id_419": { "EN": "Floatzel", @@ -2300,7 +2300,7 @@ "EN": "Cherubi", "FR": "Ceribou", "DE": "Kikugi", - "RU": "Чераби" + "RU": "Черуби" }, "pokemon_id_421": { "EN": "Cherrim", @@ -2341,7 +2341,7 @@ "EN": "Buneary", "FR": "Laporeille", "DE": "Haspiror", - "RU": "Бунири" + "RU": "Банири" }, "pokemon_id_428": { "EN": "Lopunny", @@ -2359,13 +2359,13 @@ "EN": "Honchkrow", "FR": "Corboss", "DE": "Kramshef", - "RU": "Хончкроу" + "RU": "Ханчкроу" }, "pokemon_id_431": { "EN": "Glameow", "FR": "Chaglam", "DE": "Charmian", - "RU": "Глеймяу" + "RU": "Гламяу" }, "pokemon_id_432": { "EN": "Purugly", @@ -2410,7 +2410,7 @@ "pokemon_id_439": { "EN": "Mime Jr.", "DE": "Pantimimi", - "RU": "Майм-Джуниор" + "RU": "Майм-джуниор" }, "pokemon_id_440": { "EN": "Happiny", @@ -2427,7 +2427,7 @@ "pokemon_id_442": { "EN": "Spiritomb", "DE": "Kryppuk", - "RU": "Спиритум" + "RU": "Спиритомб" }, "pokemon_id_443": { "EN": "Gible", @@ -2521,7 +2521,7 @@ "EN": "Snover", "FR": "Blizzi", "DE": "Shnebedeck", - "RU": "Сноувер" + "RU": "Сновер" }, "pokemon_id_460": { "EN": "Abomasnow", @@ -2556,7 +2556,7 @@ "EN": "Tangrowth", "FR": "Bouldeneu", "DE": "Tangoloss", - "RU": "Тэнгроут" + "RU": "Тангроут" }, "pokemon_id_466": { "EN": "Electivire", @@ -2594,7 +2594,7 @@ "EN": "Gliscor", "FR": "Scorvol", "DE": "Skorgro", - "RU": "Глайскор" + "RU": "Глискор" }, "pokemon_id_473": { "EN": "Mamoswine", @@ -2604,7 +2604,7 @@ }, "pokemon_id_474": { "EN": "Porygon-Z", - "RU": "Поригон-Z" + "RU": "ПоригонZ" }, "pokemon_id_475": { "EN": "Gallade", @@ -2651,7 +2651,7 @@ "EN": "Azelf", "FR": "Créfadet", "DE": "Tobutz", - "RU": "Азелф" + "RU": "Азельф" }, "pokemon_id_483": { "EN": "Dialga", @@ -2765,7 +2765,7 @@ "EN": "Watchog", "FR": "Miradar", "DE": "Kukmarda", - "RU": "Уочхог" + "RU": "Вочхог" }, "pokemon_id_506": { "EN": "Lillipup", @@ -2777,7 +2777,7 @@ "EN": "Herdier", "FR": "Ponchien", "DE": "Terribark", - "RU": "Хардиер" + "RU": "Хердиер" }, "pokemon_id_508": { "EN": "Stoutland", @@ -2795,7 +2795,7 @@ "EN": "Liepard", "FR": "Léopardus", "DE": "Kleoparda", - "RU": "Лайпард" + "RU": "Липард" }, "pokemon_id_511": { "EN": "Pansage", @@ -2860,7 +2860,7 @@ "EN": "Unfezant", "FR": "Déflaisan", "DE": "Fasasnob", - "RU": "Унфезант" + "RU": "Анфезант" }, "pokemon_id_522": { "EN": "Blitzle", @@ -2908,7 +2908,7 @@ "EN": "Drilbur", "FR": "Rototaupe", "DE": "Rotomurf", - "RU": "Дрилбур" + "RU": "Дрильбур" }, "pokemon_id_530": { "EN": "Excadrill", @@ -2926,7 +2926,7 @@ "EN": "Timburr", "FR": "Charpenti", "DE": "Praktibalk", - "RU": "Тимбёрр" + "RU": "Тимбурр" }, "pokemon_id_533": { "EN": "Gurdurr", @@ -2938,7 +2938,7 @@ "EN": "Conkeldurr", "FR": "Bétochef", "DE": "Meistagrif", - "RU": "Конкельдар" + "RU": "Конкльдурр" }, "pokemon_id_535": { "EN": "Tympole", @@ -2950,13 +2950,13 @@ "EN": "Palpitoad", "FR": "Batracné", "DE": "Mebrana", - "RU": "Палпитоуд" + "RU": "Палпитоад" }, "pokemon_id_537": { "EN": "Seismitoad", "FR": "Crapustule", "DE": "Branawarz", - "RU": "Сейсмитоуд" + "RU": "Сейсмитоад" }, "pokemon_id_538": { "EN": "Throh", @@ -3064,7 +3064,7 @@ "EN": "Darmanitan", "FR": "Darumacho", "DE": "Flampivian", - "RU": "Дарманитан" + "RU": "Дармантан" }, "pokemon_id_556": { "EN": "Maractus", @@ -3174,19 +3174,19 @@ "EN": "Gothita", "FR": "Scrutella", "DE": "Mollimorba", - "RU": "Гофита" + "RU": "Готита" }, "pokemon_id_575": { "EN": "Gothorita", "FR": "Mesmérella", "DE": "Hypnomorba", - "RU": "Гофорита" + "RU": "Готорита" }, "pokemon_id_576": { "EN": "Gothitelle", "FR": "Sidérella", "DE": "Morbitesse", - "RU": "Гофителль" + "RU": "Готителль" }, "pokemon_id_577": { "EN": "Solosis", @@ -3388,13 +3388,13 @@ "EN": "Axew", "FR": "Coupenotte", "DE": "Milza", - "RU": "Эксью" + "RU": "Аксью" }, "pokemon_id_611": { "EN": "Fraxure", "FR": "Incisache", "DE": "Sharfax", - "RU": "Фрэкшур" + "RU": "Фрэксюр" }, "pokemon_id_612": { "EN": "Haxorus", @@ -3538,7 +3538,7 @@ "EN": "Hydreigon", "FR": "Trioxhydre", "DE": "Trikephalo", - "RU": "Хайдрайгон" + "RU": "Хайдрагон" }, "pokemon_id_636": { "EN": "Larvesta", @@ -3568,7 +3568,7 @@ "EN": "Virizion", "FR": "Viridium", "DE": "Viridium", - "RU": "Вирижион" + "RU": "Виризион" }, "pokemon_id_641": { "EN": "Tornadus", @@ -3598,7 +3598,7 @@ }, "pokemon_id_646": { "EN": "Kyurem", - "RU": "Курем" + "RU": "Кьюрем" }, "pokemon_id_647": { "EN": "Keldeo", @@ -3610,7 +3610,7 @@ }, "pokemon_id_649": { "EN": "Genesect", - "RU": "Дженесект" + "RU": "Генесект" }, "pokemon_id_650": { "EN": "Chespin", @@ -4132,21 +4132,18 @@ "RU": "Крабролер" }, "pokemon_id_740": { - "PT-BR": "Crabominável", "EN": "Crabominable", "DE": "Krawell", "IT": "Crabominabile", "RU": "Крабаминабл" }, "pokemon_id_741": { - "PT-BR": "Oricório", "EN": "Oricorio", "FR": "Plumeline", "DE": "Choreogel", "RU": "Орикорио" }, "pokemon_id_742": { - "PT-BR": "Fofinho", "EN": "Cutiefly", "FR": "Bombydou", "DE": "Wommel", @@ -4391,19 +4388,19 @@ "EN": "Jangmo-o", "FR": "Bébécaille", "DE": "Miniras", - "RU": "Джангмо-о" + "RU": "Джангмоо" }, "pokemon_id_783": { "EN": "Hakamo-o", "FR": "Écaïd", "DE": "Mediras", - "RU": "Хакамо-o" + "RU": "Хакамоo" }, "pokemon_id_784": { "EN": "Kommo-o", "FR": "Ékaïser", "DE": "Grandiras", - "RU": "Коммо-o" + "RU": "Коммоo" }, "pokemon_id_785": { "EN": "Tapu Koko", @@ -5059,5 +5056,813 @@ "FR": "Sylveroy", "DE": "Coronospa", "RU": "Калирекс" + }, + "pokemon_id_899": { + "EN": "Wyrdeer", + "FR": "Cerbyllin", + "DE": "Damythir", + "RU": "Вирдир" + }, + "pokemon_id_900": { + "EN": "Kleavor", + "FR": "Hachécateur", + "DE": "Axantor", + "RU": "Кливор" + }, + "pokemon_id_901": { + "EN": "Ursaluna", + "FR": "Ursaking", + "RU": "Урсалуна" + }, + "pokemon_id_902": { + "EN": "Basculegion", + "FR": "Paragruel", + "DE": "Salmagnis", + "RU": "Баскулиджен" + }, + "pokemon_id_903": { + "EN": "Sneasler", + "FR": "Farfurex", + "DE": "Snieboss", + "RU": "Снизлер" + }, + "pokemon_id_904": { + "EN": "Overqwil", + "FR": "Qwilpik", + "DE": "Myriador", + "RU": "Оверквил" + }, + "pokemon_id_905": { + "EN": "Enamorus", + "FR": "Amovénus", + "DE": "Cupidos", + "RU": "Энаморус" + }, + "pokemon_id_906": { + "EN": "Sprigatito", + "FR": "Poussacha", + "DE": "Felori", + "RU": "Спригатито" + }, + "pokemon_id_907": { + "EN": "Floragato", + "FR": "Matourgeon", + "DE": "Feliospa", + "RU": "Флорагато" + }, + "pokemon_id_908": { + "EN": "Meowscarada", + "FR": "Miascarade", + "DE": "Maskagato", + "RU": "Мяускарада" + }, + "pokemon_id_909": { + "EN": "Fuecoco", + "FR": "Chochodile", + "DE": "Krokel", + "RU": "Фуэкоко" + }, + "pokemon_id_910": { + "EN": "Crocalor", + "FR": "Crocogril", + "DE": "Lokroko", + "RU": "Крокалор" + }, + "pokemon_id_911": { + "EN": "Skeledirge", + "FR": "Flâmigator", + "DE": "Skelokrok", + "RU": "Скеледирж" + }, + "pokemon_id_912": { + "EN": "Quaxly", + "FR": "Coiffeton", + "DE": "Kwaks", + "RU": "Куаксли" + }, + "pokemon_id_913": { + "EN": "Quaxwell", + "FR": "Canarbello", + "DE": "Fuentente", + "RU": "Куаксвелл" + }, + "pokemon_id_914": { + "EN": "Quaquaval", + "FR": "Palmaval", + "DE": "Bailonda", + "RU": "Куаквавал" + }, + "pokemon_id_915": { + "EN": "Lechonk", + "FR": "Gourmelet", + "DE": "Ferkuli", + "RU": "Лечонк" + }, + "pokemon_id_916": { + "EN": "Oinkologne", + "FR": "Fragroin", + "DE": "Fragrunz", + "RU": "Ойнколон" + }, + "pokemon_id_917": { + "EN": "Tarountula", + "FR": "Tissenboule", + "DE": "Tarundel", + "RU": "Тараунтула" + }, + "pokemon_id_918": { + "EN": "Spidops", + "FR": "Filentrappe", + "DE": "Spinsidias", + "RU": "Спайдопс" + }, + "pokemon_id_919": { + "EN": "Nymble", + "FR": "Lilliterelle", + "DE": "Micrick", + "RU": "Нимбл" + }, + "pokemon_id_920": { + "EN": "Lokix", + "FR": "Gambex", + "DE": "Lextremo", + "RU": "Локикс" + }, + "pokemon_id_921": { + "EN": "Pawmi", + "FR": "Pohm", + "DE": "Pamo", + "RU": "Поми" + }, + "pokemon_id_922": { + "EN": "Pawmo", + "FR": "Pohmotte", + "DE": "Pamamo", + "RU": "Помо" + }, + "pokemon_id_923": { + "EN": "Pawmot", + "FR": "Pohmarmotte", + "DE": "Pamomamo", + "RU": "Помот" + }, + "pokemon_id_924": { + "EN": "Tandemaus", + "FR": "Compagnol", + "DE": "Zwieps", + "RU": "Тандемаус" + }, + "pokemon_id_925": { + "EN": "Maushold", + "FR": "Famignol", + "DE": "Famieps", + "RU": "Маусхолд" + }, + "pokemon_id_926": { + "EN": "Fidough", + "FR": "Pâtachiot", + "DE": "Hefel", + "RU": "Файдо" + }, + "pokemon_id_927": { + "EN": "Dachsbun", + "FR": "Briochien", + "DE": "Backel", + "RU": "Даксбан" + }, + "pokemon_id_928": { + "EN": "Smoliv", + "FR": "Olivini", + "DE": "Olini", + "RU": "Смолив" + }, + "pokemon_id_929": { + "EN": "Dolliv", + "FR": "Olivado", + "DE": "Olivinio", + "RU": "Доллив" + }, + "pokemon_id_930": { + "EN": "Arboliva", + "DE": "Olithena", + "RU": "Арболива" + }, + "pokemon_id_931": { + "EN": "Squawkabilly", + "FR": "Tapatoès", + "DE": "Krawalloro", + "RU": "Сквакабилли" + }, + "pokemon_id_932": { + "EN": "Nacli", + "FR": "Selutin", + "DE": "Geosali", + "RU": "Накли" + }, + "pokemon_id_933": { + "EN": "Naclstack", + "FR": "Amassel", + "DE": "Sedisal", + "RU": "Наклстак" + }, + "pokemon_id_934": { + "EN": "Garganacl", + "FR": "Gigansel", + "DE": "Saltigant", + "RU": "Гарганакл" + }, + "pokemon_id_935": { + "EN": "Charcadet", + "FR": "Charbambin", + "DE": "Knarbon", + "RU": "Чаркадет" + }, + "pokemon_id_936": { + "EN": "Armarouge", + "FR": "Carmadura", + "DE": "Crimanzo", + "RU": "Армаруж" + }, + "pokemon_id_937": { + "EN": "Ceruledge", + "FR": "Malvalame", + "DE": "Azugladis", + "RU": "Серуледж" + }, + "pokemon_id_938": { + "EN": "Tadbulb", + "FR": "Têtampoule", + "DE": "Blipp", + "RU": "Тадбалб" + }, + "pokemon_id_939": { + "EN": "Bellibolt", + "FR": "Ampibidou", + "DE": "Wampitz", + "RU": "Беллиболт" + }, + "pokemon_id_940": { + "EN": "Wattrel", + "FR": "Zapétrel", + "DE": "Voltrel", + "RU": "Ваттрел" + }, + "pokemon_id_941": { + "EN": "Kilowattrel", + "FR": "Fulgulairo", + "DE": "Voltrean", + "RU": "Киловаттрел" + }, + "pokemon_id_942": { + "EN": "Maschiff", + "FR": "Grondogue", + "DE": "Mobtiff", + "RU": "Масчифф" + }, + "pokemon_id_943": { + "EN": "Mabosstiff", + "FR": "Dogrino", + "DE": "Mastifioso", + "RU": "Мабосстифф" + }, + "pokemon_id_944": { + "EN": "Shroodle", + "FR": "Gribouraigne", + "DE": "Sproxi", + "RU": "Шрудл" + }, + "pokemon_id_945": { + "EN": "Grafaiai", + "FR": "Tag-Tag", + "DE": "Affiti", + "RU": "Графайай" + }, + "pokemon_id_946": { + "EN": "Bramblin", + "FR": "Virovent", + "DE": "Weherba", + "RU": "Брамблин" + }, + "pokemon_id_947": { + "EN": "Brambleghast", + "FR": "Virevorreur", + "DE": "Horrerba", + "RU": "Брамблгаст" + }, + "pokemon_id_948": { + "EN": "Toedscool", + "FR": "Terracool", + "DE": "Tentagra", + "RU": "Тодскул" + }, + "pokemon_id_949": { + "EN": "Toedscruel", + "FR": "Terracruel", + "DE": "Tenterra", + "RU": "Тодскрул" + }, + "pokemon_id_950": { + "EN": "Klawf", + "FR": "Craparoi", + "DE": "Klibbe", + "RU": "Клоф" + }, + "pokemon_id_951": { + "EN": "Capsakid", + "FR": "Pimito", + "DE": "Chilingel", + "RU": "Капсакид" + }, + "pokemon_id_952": { + "EN": "Scovillain", + "FR": "Scovilain", + "DE": "Halupenjo", + "RU": "Сковиллен" + }, + "pokemon_id_953": { + "EN": "Rellor", + "FR": "Léboulérou", + "DE": "Relluk", + "RU": "Реллор" + }, + "pokemon_id_954": { + "EN": "Rabsca", + "FR": "Bérasca", + "DE": "Skarabaks", + "RU": "Рабска" + }, + "pokemon_id_955": { + "EN": "Flittle", + "FR": "Flotillon", + "DE": "Flattutu", + "RU": "Флиттл" + }, + "pokemon_id_956": { + "EN": "Espathra", + "FR": "Cléopsytra", + "DE": "Psiopatra", + "RU": "Эспатра" + }, + "pokemon_id_957": { + "EN": "Tinkatink", + "FR": "Forgerette", + "DE": "Forgita", + "RU": "Тинкатинк" + }, + "pokemon_id_958": { + "EN": "Tinkatuff", + "FR": "Forgella", + "DE": "Tafforgita", + "RU": "Тинкатафф" + }, + "pokemon_id_959": { + "EN": "Tinkaton", + "FR": "Forgelina", + "DE": "Granforgita", + "RU": "Тинкатон" + }, + "pokemon_id_960": { + "EN": "Wiglett", + "FR": "Taupikeau", + "DE": "Schligda", + "RU": "Виглетт" + }, + "pokemon_id_961": { + "EN": "Wugtrio", + "FR": "Triopikeau", + "DE": "Schligdri", + "RU": "Вагтрио" + }, + "pokemon_id_962": { + "EN": "Bombirdier", + "FR": "Lestombaile", + "DE": "Adebom", + "RU": "Бомбердир" + }, + "pokemon_id_963": { + "EN": "Finizen", + "FR": "Dofin", + "DE": "Normifin", + "RU": "Финизен" + }, + "pokemon_id_964": { + "EN": "Palafin", + "FR": "Superdofin", + "DE": "Delfinator", + "RU": "Палафин" + }, + "pokemon_id_965": { + "EN": "Varoom", + "FR": "Vrombi", + "DE": "Knattox", + "RU": "Варум" + }, + "pokemon_id_966": { + "EN": "Revavroom", + "FR": "Vrombotor", + "DE": "Knattatox", + "RU": "Реваврум" + }, + "pokemon_id_967": { + "EN": "Cyclizar", + "FR": "Motorizard", + "DE": "Mopex", + "RU": "Сайклизар" + }, + "pokemon_id_968": { + "EN": "Orthworm", + "FR": "Ferdeter", + "DE": "Schlurm", + "RU": "Ортворм" + }, + "pokemon_id_969": { + "EN": "Glimmet", + "FR": "Germéclat", + "DE": "Lumispross", + "RU": "Глиммет" + }, + "pokemon_id_970": { + "EN": "Glimmora", + "FR": "Floréclat", + "DE": "Lumiflora", + "RU": "Глиммора" + }, + "pokemon_id_971": { + "EN": "Greavard", + "FR": "Toutombe", + "DE": "Gruff", + "RU": "Гриверд" + }, + "pokemon_id_972": { + "EN": "Houndstone", + "FR": "Tomberro", + "DE": "Friedwuff", + "RU": "Хаундстоун" + }, + "pokemon_id_973": { + "EN": "Flamigo", + "FR": "Flamenroule", + "DE": "Flaminkno", + "RU": "Фламиго" + }, + "pokemon_id_974": { + "EN": "Cetoddle", + "FR": "Piétacé", + "DE": "Flaniwal", + "RU": "Сетоддл" + }, + "pokemon_id_975": { + "EN": "Cetitan", + "FR": "Balbalèze", + "DE": "Kolowal", + "RU": "Сетайтан" + }, + "pokemon_id_976": { + "EN": "Veluza", + "FR": "Délestin", + "DE": "Agiluza", + "RU": "Велуза" + }, + "pokemon_id_977": { + "EN": "Dondozo", + "FR": "Oyacata", + "DE": "Heerashai", + "RU": "Дондозо" + }, + "pokemon_id_978": { + "EN": "Tatsugiri", + "FR": "Nigirigon", + "DE": "Nigiragi", + "RU": "Тацугири" + }, + "pokemon_id_979": { + "EN": "Annihilape", + "FR": "Courrousinge", + "DE": "Epitaff", + "RU": "Аннаялейп" + }, + "pokemon_id_980": { + "EN": "Clodsire", + "FR": "Terraiste", + "DE": "Suelord", + "RU": "Клодсайр" + }, + "pokemon_id_981": { + "EN": "Farigiraf", + "RU": "Фарижираф" + }, + "pokemon_id_982": { + "EN": "Dudunsparce", + "FR": "Deusolourdo", + "DE": "Dummimisel", + "RU": "Даданспарс" + }, + "pokemon_id_983": { + "EN": "Kingambit", + "FR": "Scalpereur", + "DE": "Gladimperio", + "RU": "Кингамбит" + }, + "pokemon_id_984": { + "PT-BR": "Presa Grande", + "EN": "Great Tusk", + "FR": "Fort-Ivoire", + "DE": "Riesenzahn", + "IT": "Grandizanne", + "RU": "Великий Бивень", + "ES": "Colmilargo" + }, + "pokemon_id_985": { + "PT-BR": "Cauda Brado", + "EN": "Scream Tail", + "FR": "Hurle-Queue", + "DE": "Brüllschweif", + "IT": "Codaurlante", + "RU": "Кричащий Хвост", + "ES": "Colagrito" + }, + "pokemon_id_986": { + "PT-BR": "Capuz Bruto", + "EN": "Brute Bonnet", + "FR": "Fongus-Furie", + "DE": "Wutpilz", + "IT": "Fungofurioso", + "RU": "Свирепый Гриб", + "ES": "Furioseta" + }, + "pokemon_id_987": { + "PT-BR": "Juba Sopro", + "EN": "Flutter Mane", + "FR": "Flotte-Mèche", + "DE": "Flatterhaar", + "IT": "Crinealato", + "RU": "Волнистая Грива", + "ES": "Melenaleteo" + }, + "pokemon_id_988": { + "PT-BR": "Asa Rasteira", + "EN": "Slither Wing", + "FR": "Rampe-Ailes", + "DE": "Kriechflügel", + "IT": "Alirasenti", + "RU": "Ползущее Крыло", + "ES": "Reptalada" + }, + "pokemon_id_989": { + "PT-BR": "Choque Areia", + "EN": "Sandy Shocks", + "FR": "Pelage-Sablé", + "DE": "Sandfell", + "IT": "Peldisabbia", + "RU": "Магнитные Вихры", + "ES": "Pelarena" + }, + "pokemon_id_990": { + "PT-BR": "Trilho Férreo", + "EN": "Iron Treads", + "FR": "Roue-de-Fer", + "DE": "Eisenrad", + "IT": "Solcoferreo", + "RU": "Железные Гусеницы", + "ES": "Ferrodada" + }, + "pokemon_id_991": { + "PT-BR": "Pacote Férreo", + "EN": "Iron Bundle", + "FR": "Hotte-de-Fer", + "DE": "Eisenbündel", + "IT": "Saccoferreo", + "RU": "Железный Мешок", + "ES": "Ferrosaco" + }, + "pokemon_id_992": { + "PT-BR": "Mãos Férreas", + "EN": "Iron Hands", + "FR": "Paume-de-Fer", + "DE": "Eisenhand", + "IT": "Manoferrea", + "RU": "Железные Руки", + "ES": "Ferropalmas" + }, + "pokemon_id_993": { + "PT-BR": "Jugulares Férreas", + "EN": "Iron Jugulis", + "FR": "Têtes-de-Fer", + "DE": "Eisenhals", + "IT": "Colloferreo", + "RU": "Железные Головы", + "ES": "Ferrocuello" + }, + "pokemon_id_994": { + "PT-BR": "Mariposa Férrea", + "EN": "Iron Moth", + "FR": "Mite-de-Fer", + "DE": "Eisenfalter", + "IT": "Falenaferrea", + "RU": "Железный Мотылёк", + "ES": "Ferropolilla" + }, + "pokemon_id_995": { + "PT-BR": "Espinhos Férreos", + "EN": "Iron Thorns", + "FR": "Épine-de-Fer", + "DE": "Eisendorn", + "IT": "Spineferree", + "RU": "Железные Шипы", + "ES": "Ferropúas" + }, + "pokemon_id_996": { + "EN": "Frigibax", + "FR": "Frigodo", + "DE": "Frospino", + "RU": "Фриджибакс" + }, + "pokemon_id_997": { + "EN": "Arctibax", + "FR": "Cryodo", + "DE": "Cryospino", + "RU": "Арктибакс" + }, + "pokemon_id_998": { + "EN": "Baxcalibur", + "FR": "Glaivodo", + "DE": "Espinodon", + "RU": "Бакскалибур" + }, + "pokemon_id_999": { + "EN": "Gimmighoul", + "FR": "Mordudor", + "DE": "Gierspenst", + "RU": "Гиммигул" + }, + "pokemon_id_1000": { + "EN": "Gholdengo", + "FR": "Gromago", + "DE": "Monetigo", + "RU": "Голденго" + }, + "pokemon_id_1001": { + "EN": "Wo-Chien", + "FR": "Chongjian", + "DE": "Chongjian", + "RU": "Во-Чиен" + }, + "pokemon_id_1002": { + "EN": "Chien-Pao", + "FR": "Baojian", + "DE": "Baojian", + "RU": "Чиен-Пао" + }, + "pokemon_id_1003": { + "EN": "Ting-Lu", + "FR": "Dinglu", + "DE": "Dinglu", + "RU": "Тинг-Лу" + }, + "pokemon_id_1004": { + "EN": "Chi-Yu", + "FR": "Yuyu", + "DE": "Yuyu", + "RU": "Чи-Ю" + }, + "pokemon_id_1005": { + "PT-BR": "Lua Estrondo", + "EN": "Roaring Moon", + "FR": "Rugit-Lune", + "DE": "Donnersichel", + "IT": "Lunaruggente", + "RU": "Ревущий Месяц", + "ES": "Bramaluna" + }, + "pokemon_id_1006": { + "PT-BR": "Valentia Férrea", + "EN": "Iron Valiant", + "FR": "Garde-de-Fer", + "DE": "Eisenkrieger", + "IT": "Eroeferreo", + "RU": "Железный Воин", + "ES": "Ferropaladín" + }, + "pokemon_id_1007": { + "EN": "Koraidon", + "RU": "Корайдон" + }, + "pokemon_id_1008": { + "EN": "Miraidon", + "RU": "Мирайдон" + }, + "pokemon_id_1009": { + "PT-BR": "Onda Ando", + "EN": "Walking Wake", + "FR": "Serpente-Eau", + "DE": "Windewoge", + "IT": "Acquecrespe", + "RU": "Вздымающаяся Волна", + "ES": "Ondulagua" + }, + "pokemon_id_1010": { + "PT-BR": "Folhas Férreas", + "EN": "Iron Leaves", + "FR": "Vert-de-Fer", + "DE": "Eisenblatt", + "IT": "Fogliaferrea", + "RU": "Железные Листья", + "ES": "Ferroverdor" + }, + "pokemon_id_1011": { + "EN": "Dipplin", + "FR": "Pomdramour", + "DE": "Sirapfel", + "RU": "Дипплин" + }, + "pokemon_id_1012": { + "EN": "Poltchageist", + "DE": "Mortcha", + "RU": "Полчагейст" + }, + "pokemon_id_1013": { + "EN": "Sinistcha", + "FR": "Théffroyable", + "DE": "Fatalitcha", + "RU": "Синисча" + }, + "pokemon_id_1014": { + "EN": "Okidogi", + "FR": "Félicanis", + "DE": "Boninu", + "RU": "Окидоги" + }, + "pokemon_id_1015": { + "EN": "Munkidori", + "FR": "Fortusimia", + "DE": "Benesaru", + "RU": "Манкидори" + }, + "pokemon_id_1016": { + "EN": "Fezandipiti", + "FR": "Favianos", + "DE": "Beatori", + "RU": "Фезандипити" + }, + "pokemon_id_1017": { + "EN": "Ogerpon", + "RU": "Огерпон" + }, + "pokemon_id_1018": { + "EN": "Archaludon", + "FR": "Pondralugon", + "DE": "Briduradon", + "RU": "Архалюдон" + }, + "pokemon_id_1019": { + "EN": "Hydrapple", + "FR": "Pomdorochi", + "DE": "Hydrapfel", + "RU": "Гидраппл" + }, + "pokemon_id_1020": { + "PT-BR": "Fogo Corrosão", + "EN": "Gouging Fire", + "DE": "Keilflamme", + "IT": "Vampeaguzze", + "RU": "Пронзающее Пламя", + "ES": "Flamariete" + }, + "pokemon_id_1021": { + "PT-BR": "Raio Fúria", + "EN": "Raging Bolt", + "FR": "Ire-Foudre", + "DE": "Furienblitz", + "IT": "Furiatonante", + "RU": "Яростная Молния", + "ES": "Electrofuria" + }, + "pokemon_id_1022": { + "PT-BR": "Rocha Férrea", + "EN": "Iron Boulder", + "FR": "Roc-de-Fer", + "DE": "Eisenfels", + "IT": "Massoferreo", + "RU": "Железный Валун", + "ES": "Ferromole" + }, + "pokemon_id_1023": { + "PT-BR": "Chifres Férreos", + "EN": "Iron Crown", + "FR": "Chef-de-Fer", + "DE": "Eisenhaupt", + "IT": "Capoferreo", + "RU": "Железный Вождь", + "ES": "Ferrotesta" + }, + "pokemon_id_1024": { + "EN": "Terapagos", + "RU": "Терапагос" + }, + "pokemon_id_1025": { + "EN": "Pecharunt", + "FR": "Pêchaminus", + "DE": "Infamomo", + "RU": "Печарант" } } \ No newline at end of file diff --git a/lang/pokemon_forms.json b/lang/pokemon_forms.json new file mode 100644 index 00000000..d17afe1d --- /dev/null +++ b/lang/pokemon_forms.json @@ -0,0 +1,2631 @@ +{ + "pokemon_form_1": { + "EN": "A" + }, + "pokemon_form_2": { + "EN": "B" + }, + "pokemon_form_3": { + "EN": "C" + }, + "pokemon_form_4": { + "EN": "D" + }, + "pokemon_form_5": { + "EN": "E" + }, + "pokemon_form_6": { + "EN": "F" + }, + "pokemon_form_7": { + "EN": "G" + }, + "pokemon_form_8": { + "EN": "H" + }, + "pokemon_form_9": { + "EN": "I" + }, + "pokemon_form_10": { + "EN": "J" + }, + "pokemon_form_11": { + "EN": "K" + }, + "pokemon_form_12": { + "EN": "L" + }, + "pokemon_form_13": { + "EN": "M" + }, + "pokemon_form_14": { + "EN": "N" + }, + "pokemon_form_15": { + "EN": "O" + }, + "pokemon_form_16": { + "EN": "P" + }, + "pokemon_form_17": { + "EN": "Q" + }, + "pokemon_form_18": { + "EN": "R" + }, + "pokemon_form_19": { + "EN": "S" + }, + "pokemon_form_20": { + "EN": "T" + }, + "pokemon_form_21": { + "EN": "U" + }, + "pokemon_form_22": { + "EN": "V" + }, + "pokemon_form_23": { + "EN": "W" + }, + "pokemon_form_24": { + "EN": "X" + }, + "pokemon_form_25": { + "EN": "Y" + }, + "pokemon_form_26": { + "EN": "Z" + }, + "pokemon_form_27": { + "EN": "Exclamation Point", + "FR": "!", + "DE": "!", + "RU": "!" + }, + "pokemon_form_28": { + "EN": "Question Mark", + "FR": "?", + "DE": "?", + "RU": "?" + }, + "pokemon_form_30": { + "EN": "Sunny", + "FR": "Solaire", + "DE": "Sonnenform", + "RU": "Солнечная форма" + }, + "pokemon_form_31": { + "EN": "Rainy", + "FR": "Eau de pluie", + "DE": "Regenform", + "RU": "Дождливая форма" + }, + "pokemon_form_32": { + "EN": "Snowy", + "FR": "Blizzard", + "DE": "Schneeform", + "RU": "Снежная форма" + }, + "pokemon_form_34": { + "PT-BR": "Ataque", + "EN": "Attack", + "FR": "Attaque", + "DE": "Angriffsform", + "IT": "Attacco", + "RU": "Атакующая форма", + "ES": "Ataque" + }, + "pokemon_form_35": { + "PT-BR": "Defesa", + "EN": "Defense", + "FR": "Défense", + "DE": "Verteidigungsform", + "IT": "Difesa", + "RU": "Защитная форма", + "ES": "Defensa" + }, + "pokemon_form_36": { + "PT-BR": "Velocidade", + "EN": "Speed", + "FR": "Vitesse", + "DE": "Initiativeform", + "IT": "Velocità", + "RU": "Скоростная форма", + "ES": "Velocidad" + }, + "pokemon_form_37": { + "EN": "01" + }, + "pokemon_form_38": { + "EN": "02" + }, + "pokemon_form_39": { + "EN": "03" + }, + "pokemon_form_40": { + "EN": "04" + }, + "pokemon_form_41": { + "EN": "05" + }, + "pokemon_form_42": { + "EN": "06" + }, + "pokemon_form_43": { + "EN": "07" + }, + "pokemon_form_44": { + "EN": "08" + }, + "pokemon_form_46": { + "EN": "Alola", + "RU": "Алольская форма" + }, + "pokemon_form_48": { + "EN": "Alola", + "RU": "Алольская форма" + }, + "pokemon_form_50": { + "EN": "Alola", + "RU": "Алольская форма" + }, + "pokemon_form_52": { + "EN": "Alola", + "RU": "Алольская форма" + }, + "pokemon_form_54": { + "EN": "Alola", + "RU": "Алольская форма" + }, + "pokemon_form_56": { + "EN": "Alola", + "RU": "Алольская форма" + }, + "pokemon_form_58": { + "EN": "Alola", + "RU": "Алольская форма" + }, + "pokemon_form_60": { + "EN": "Alola", + "RU": "Алольская форма" + }, + "pokemon_form_62": { + "EN": "Alola", + "RU": "Алольская форма" + }, + "pokemon_form_64": { + "EN": "Alola", + "RU": "Алольская форма" + }, + "pokemon_form_66": { + "EN": "Alola", + "RU": "Алольская форма" + }, + "pokemon_form_68": { + "EN": "Alola", + "RU": "Алольская форма" + }, + "pokemon_form_70": { + "EN": "Alola", + "RU": "Алольская форма" + }, + "pokemon_form_72": { + "EN": "Alola", + "RU": "Алольская форма" + }, + "pokemon_form_74": { + "EN": "Alola", + "RU": "Алольская форма" + }, + "pokemon_form_76": { + "EN": "Alola", + "RU": "Алольская форма" + }, + "pokemon_form_78": { + "EN": "Alola", + "RU": "Алольская форма" + }, + "pokemon_form_80": { + "EN": "Alola", + "RU": "Алольская форма" + }, + "pokemon_form_82": { + "PT-BR": "Rotom Congelante", + "EN": "Frost Rotom", + "FR": "Motisma Froid", + "DE": "Frost", + "IT": "Rotom Gelo", + "RU": "Холодильник", + "ES": "Rotom Frío" + }, + "pokemon_form_83": { + "PT-BR": "Rotom Ventilador", + "EN": "Fan Rotom", + "FR": "Motisma Hélice", + "DE": "Wirbel", + "IT": "Rotom Vortice", + "RU": "Вентилятор", + "ES": "Rotom Ventilador" + }, + "pokemon_form_84": { + "PT-BR": "Rotom Corte", + "EN": "Mow Rotom", + "FR": "Motisma Tonte", + "DE": "Schneid", + "IT": "Rotom Taglio", + "RU": "Газонокосилка", + "ES": "Rotom Corte" + }, + "pokemon_form_85": { + "PT-BR": "Rotom Lavagem", + "EN": "Wash Rotom", + "FR": "Motisma Lavage", + "DE": "Wasch", + "IT": "Rotom Lavaggio", + "RU": "Стиралка", + "ES": "Rotom Lavado" + }, + "pokemon_form_86": { + "PT-BR": "Rotom Calor", + "EN": "Heat Rotom", + "FR": "Motisma Chaleur", + "DE": "Hitze", + "IT": "Rotom Calore", + "RU": "Обогреватель", + "ES": "Rotom Calor" + }, + "pokemon_form_87": { + "PT-BR": "Manto Vegetal", + "EN": "Plant Cloak", + "FR": "Plante", + "DE": "Pflanzenumhang", + "IT": "Manto Pianta", + "RU": "Растительный покров", + "ES": "Tronco Planta" + }, + "pokemon_form_88": { + "PT-BR": "Manto Arenoso", + "EN": "Sandy Cloak", + "FR": "Cape Sable", + "DE": "Sandumhang", + "IT": "Manto Sabbia", + "RU": "Песчанный покров", + "ES": "Tronco Arena" + }, + "pokemon_form_89": { + "PT-BR": "Manto de Lixo", + "EN": "Trash Cloak", + "FR": "Cape Déchet", + "DE": "Lumpenumhang", + "IT": "Manto Scarti", + "RU": "Мусорный покров", + "ES": "Tronco Basura" + }, + "pokemon_form_90": { + "PT-BR": "Forma Alterada", + "EN": "Altered Forme", + "FR": "Forme Alternative", + "DE": "Wandel", + "IT": "Forma Alterata", + "RU": "Изменённая форма", + "ES": "Forma Modificada" + }, + "pokemon_form_91": { + "PT-BR": "Forma Origem", + "EN": "Origin Forme", + "FR": "Forme Originelle", + "DE": "Ur", + "IT": "Forma Originale", + "RU": "Исходная форма", + "ES": "Forma Origen" + }, + "pokemon_form_92": { + "PT-BR": "Forma Céu", + "EN": "Sky Forme", + "FR": "Forme Céleste", + "DE": "Zenit", + "IT": "Forma Cielo", + "RU": "Небесная форма", + "ES": "Forma Cielo" + }, + "pokemon_form_93": { + "PT-BR": "Forma Terrestre", + "EN": "Land Forme", + "FR": "Forme Terrestre", + "DE": "Land", + "IT": "Forma Terra", + "RU": "Земная форма", + "ES": "Forma Tierra" + }, + "pokemon_form_94": { + "PT-BR": "Forma Nublada", + "EN": "Overcast Form", + "FR": "Temps Couvert", + "DE": "Wolkenform", + "IT": "Forma Nuvola", + "RU": "Пасмурная форма", + "ES": "Forma Encapotado" + }, + "pokemon_form_95": { + "EN": "Sunny", + "FR": "Temps Ensoleillé", + "DE": "Sonnenform", + "RU": "Солнечная форма" + }, + "pokemon_form_96": { + "PT-BR": "Mar Ocidental", + "EN": "West Sea", + "FR": "Mer Occident", + "DE": "Westliches Meer", + "IT": "Mare Ovest", + "RU": "Западно-морская форма", + "ES": "Mar Oeste" + }, + "pokemon_form_97": { + "PT-BR": "Mar Leste", + "EN": "East Sea", + "FR": "Mer Orient", + "DE": "Östliches Meer", + "IT": "Mare Est", + "RU": "Восточно-морская форма", + "ES": "Mar Este" + }, + "pokemon_form_98": { + "PT-BR": "Mar Ocidental", + "EN": "West Sea", + "FR": "Mer Occident", + "DE": "Westliches Meer", + "IT": "Mare Ovest", + "RU": "Западно-морская форма", + "ES": "Mar Oeste" + }, + "pokemon_form_99": { + "PT-BR": "Mar Leste", + "EN": "East Sea", + "FR": "Mer Orient", + "DE": "Östliches Meer", + "IT": "Mare Est", + "RU": "Восточно-морская форма", + "ES": "Mar Este" + }, + "pokemon_form_101": { + "PT-BR": "Lutador", + "EN": "Fighting", + "FR": "Combat", + "DE": "Kampf", + "IT": "Lotta", + "RU": "Боевая форма", + "ES": "Lucha" + }, + "pokemon_form_102": { + "PT-BR": "Voador", + "EN": "Flying", + "FR": "Vol", + "DE": "Flug", + "IT": "Volante", + "RU": "Летающая форма", + "ES": "Volador" + }, + "pokemon_form_103": { + "PT-BR": "Venenoso", + "EN": "Poison", + "DE": "Gift", + "IT": "Veleno", + "RU": "Ядовитая форма", + "ES": "Veneno" + }, + "pokemon_form_104": { + "PT-BR": "Terrestre", + "EN": "Ground", + "FR": "Sol", + "DE": "Boden", + "IT": "Terra", + "RU": "Земленая форма", + "ES": "Tierra" + }, + "pokemon_form_105": { + "PT-BR": "Pedra", + "EN": "Rock", + "FR": "Roche", + "DE": "Gestein", + "IT": "Roccia", + "RU": "Каменная форма", + "ES": "Roca" + }, + "pokemon_form_106": { + "PT-BR": "Inseto", + "EN": "Bug", + "FR": "Insecte", + "DE": "Käfer", + "IT": "Coleottero", + "RU": "Жучья форма", + "ES": "Bicho" + }, + "pokemon_form_107": { + "PT-BR": "Fantasma", + "EN": "Ghost", + "FR": "Spectre", + "DE": "Geist", + "IT": "Spettro", + "RU": "Призрачная форма", + "ES": "Fantasma" + }, + "pokemon_form_108": { + "PT-BR": "Aço", + "EN": "Steel", + "FR": "Acier", + "DE": "Stahl", + "IT": "Acciaio", + "RU": "Стальная форма", + "ES": "Acero" + }, + "pokemon_form_109": { + "PT-BR": "Fogo", + "EN": "Fire", + "FR": "Feu", + "DE": "Feuer", + "IT": "Fuoco", + "RU": "Огненная форма", + "ES": "Fuego" + }, + "pokemon_form_110": { + "PT-BR": "Água", + "EN": "Water", + "FR": "Eau", + "DE": "Wasser", + "IT": "Acqua", + "RU": "Водная форма", + "ES": "Agua" + }, + "pokemon_form_111": { + "PT-BR": "Planta", + "EN": "Grass", + "FR": "Plante", + "DE": "Pflanze", + "IT": "Erba", + "RU": "Травяная форма", + "ES": "Planta" + }, + "pokemon_form_112": { + "PT-BR": "Elétrico", + "EN": "Electric", + "FR": "Électrik", + "DE": "Elektro", + "IT": "Elettro", + "RU": "Электрическая форма", + "ES": "Eléctrico" + }, + "pokemon_form_113": { + "PT-BR": "Psíquico", + "EN": "Psychic", + "FR": "Psy", + "DE": "Psycho", + "IT": "Psico", + "RU": "Психическая форма", + "ES": "Psíquico" + }, + "pokemon_form_114": { + "PT-BR": "Gelo", + "EN": "Ice", + "FR": "Glace", + "DE": "Eis", + "IT": "Ghiaccio", + "RU": "Ледяная форма", + "ES": "Hielo" + }, + "pokemon_form_115": { + "PT-BR": "Dragão", + "EN": "Dragon", + "DE": "Drache", + "IT": "Drago", + "RU": "Драконья форма", + "ES": "Dragón" + }, + "pokemon_form_116": { + "PT-BR": "Sombrio", + "EN": "Dark", + "FR": "Ténèbres", + "DE": "Unlicht", + "IT": "Buio", + "RU": "Темная форма", + "ES": "Siniestro" + }, + "pokemon_form_117": { + "PT-BR": "Fada", + "EN": "Fairy", + "FR": "Fée", + "DE": "Fee", + "IT": "Folletto", + "RU": "Феечная форма", + "ES": "Hada" + }, + "pokemon_form_118": { + "PT-BR": "Manto Vegetal", + "EN": "Plant Cloak", + "FR": "Cape Plante", + "DE": "Pflanzenumhang", + "IT": "Manto Pianta", + "RU": "Растительный покров", + "ES": "Tronco Planta" + }, + "pokemon_form_119": { + "PT-BR": "Manto Arenoso", + "EN": "Sandy Cloak", + "FR": "Cape Sable", + "DE": "Sandumhang", + "IT": "Manto Sabbia", + "RU": "Песчанный покров", + "ES": "Tronco Arena" + }, + "pokemon_form_120": { + "PT-BR": "Manto de Lixo", + "EN": "Trash Cloak", + "FR": "Cape Déchet", + "DE": "Lumpenumhang", + "IT": "Manto Scarti", + "RU": "Мусорный покров", + "ES": "Tronco Basura" + }, + "pokemon_form_121": { + "EN": "09" + }, + "pokemon_form_122": { + "EN": "10" + }, + "pokemon_form_123": { + "EN": "11" + }, + "pokemon_form_124": { + "EN": "12" + }, + "pokemon_form_125": { + "EN": "13" + }, + "pokemon_form_126": { + "EN": "14" + }, + "pokemon_form_127": { + "EN": "15" + }, + "pokemon_form_128": { + "EN": "16" + }, + "pokemon_form_129": { + "EN": "17" + }, + "pokemon_form_130": { + "EN": "18" + }, + "pokemon_form_131": { + "EN": "19" + }, + "pokemon_form_132": { + "EN": "20" + }, + "pokemon_form_133": { + "EN": "A", + "FR": "Armure", + "DE": "Rüstung" + }, + "pokemon_form_136": { + "PT-BR": "Listras Vermelhas", + "EN": "Red-Striped", + "FR": "Motif Rouge", + "DE": "Rotlinig", + "IT": "Forma Linearossa", + "RU": "Красно-полосатая форма", + "ES": "Forma Raya Roja" + }, + "pokemon_form_137": { + "PT-BR": "Listras Azuis", + "EN": "Blue-Striped", + "FR": "Motif Bleu", + "DE": "Blaulinig", + "IT": "Forma Lineablu", + "RU": "Сине-полосатая форма", + "ES": "Forma Raya Azul" + }, + "pokemon_form_138": { + "EN": "Standard", + "RU": "Обычная форма" + }, + "pokemon_form_139": { + "EN": "Zen", + "DE": "Trance", + "RU": "Дзен" + }, + "pokemon_form_140": { + "PT-BR": "Forma Materializada", + "EN": "Incarnate Forme", + "FR": "Forme Avatar", + "DE": "Inkarnation", + "IT": "Forma Incarnazione", + "RU": "Воплощённая форма", + "ES": "Forma Avatar" + }, + "pokemon_form_141": { + "PT-BR": "Forma Therian", + "EN": "Therian Forme", + "FR": "Forme Totémique", + "DE": "Tiergeist", + "IT": "Forma Totem", + "RU": "Териан форма", + "ES": "Forma Tótem" + }, + "pokemon_form_142": { + "PT-BR": "Forma Materializada", + "EN": "Incarnate Forme", + "FR": "Forme Avatar", + "DE": "Inkarnation", + "IT": "Forma Incarnazione", + "RU": "Воплощённая форма", + "ES": "Forma Avatar" + }, + "pokemon_form_143": { + "PT-BR": "Forma Therian", + "EN": "Therian Forme", + "FR": "Forme Totémique", + "DE": "Tiergeist", + "IT": "Forma Totem", + "RU": "Териан форма", + "ES": "Forma Tótem" + }, + "pokemon_form_144": { + "PT-BR": "Forma Materializada", + "EN": "Incarnate Forme", + "FR": "Forme Avatar", + "DE": "Inkarnation", + "IT": "Forma Incarnazione", + "RU": "Воплощённая форма", + "ES": "Forma Avatar" + }, + "pokemon_form_145": { + "PT-BR": "Forma Therian", + "EN": "Therian Forme", + "FR": "Forme Totémique", + "DE": "Tiergeist", + "IT": "Forma Totem", + "RU": "Териан форма", + "ES": "Forma Tótem" + }, + "pokemon_form_147": { + "PT-BR": "Kyurem Preto", + "EN": "Black Kyurem", + "FR": "Kyurem Noir", + "DE": "Schwarz", + "IT": "Kyurem Nero", + "RU": "Чёрный Кьюрем", + "ES": "Kyurem Negro" + }, + "pokemon_form_148": { + "PT-BR": "Kyurem Branco", + "EN": "White Kyurem", + "FR": "Kyurem Blanc", + "DE": "Weiß", + "IT": "Kyurem Bianco", + "RU": "Белый Кьюрем", + "ES": "Kyurem Blanco" + }, + "pokemon_form_149": { + "PT-BR": "Forma Comum", + "EN": "Ordinary Form", + "FR": "Aspect Normal", + "DE": "Standard", + "IT": "Forma Normale", + "RU": "Обычная форма", + "ES": "Forma Habitual" + }, + "pokemon_form_150": { + "PT-BR": "Forma Resoluta", + "EN": "Resolute Form", + "FR": "Resolute", + "DE": "Resolut", + "IT": "Forma Risoluta", + "RU": "Решительная форма", + "ES": "Forma Brío" + }, + "pokemon_form_151": { + "PT-BR": "Forma Ária", + "EN": "Aria Forme", + "FR": "Forme Chant", + "DE": "Gesang", + "IT": "Forma Canto", + "RU": "Форма Арии", + "ES": "Forma Lírica" + }, + "pokemon_form_152": { + "PT-BR": "Forma Pirueta", + "EN": "Pirouette Forme", + "FR": "Pirouette", + "DE": "Tanz", + "IT": "Forma Danza", + "RU": "Форма Пируэта", + "ES": "Forma Danza" + }, + "pokemon_form_585": { + "PT-BR": "Forma Primavera", + "EN": "Spring Form", + "FR": "Forme Printemps", + "DE": "Frühling", + "IT": "Forma Primavera", + "RU": "Весенняя форма", + "ES": "Forma Primavera" + }, + "pokemon_form_586": { + "PT-BR": "Forma Verão", + "EN": "Summer Form", + "FR": "Forme Été", + "DE": "Sommer", + "IT": "Forma Estate", + "RU": "Летняя форма", + "ES": "Forma Verano" + }, + "pokemon_form_587": { + "PT-BR": "Forma Outono", + "EN": "Autumn Form", + "FR": "Forme Automne", + "DE": "Herbst", + "IT": "Forma Autunno", + "RU": "Осенняя форма", + "ES": "Forma Otoño" + }, + "pokemon_form_588": { + "PT-BR": "Forma Inverno", + "EN": "Winter Form", + "FR": "Forme Hiver", + "DE": "Winter", + "IT": "Forma Inverno", + "RU": "Зимняя форма", + "ES": "Forma Invierno" + }, + "pokemon_form_589": { + "PT-BR": "Forma Primavera", + "EN": "Spring Form", + "FR": "Forme Printemps", + "DE": "Frühling", + "IT": "Forma Primavera", + "RU": "Весенняя форма", + "ES": "Forma Primavera" + }, + "pokemon_form_590": { + "PT-BR": "Forma Verão", + "EN": "Summer Form", + "FR": "Forme Été", + "DE": "Sommer", + "IT": "Forma Estate", + "RU": "Летняя форма", + "ES": "Forma Verano" + }, + "pokemon_form_591": { + "PT-BR": "Forma Outono", + "EN": "Autumn Form", + "FR": "Forme Automne", + "DE": "Herbst", + "IT": "Forma Autunno", + "RU": "Осенняя форма", + "ES": "Forma Otoño" + }, + "pokemon_form_592": { + "PT-BR": "Forma Inverno", + "EN": "Winter Form", + "FR": "Forme Hiver", + "DE": "Winter", + "IT": "Forma Inverno", + "RU": "Зимняя форма", + "ES": "Forma Invierno" + }, + "pokemon_form_594": { + "EN": "Shock", + "FR": "Module Choc", + "DE": "Blitzmodul", + "RU": "Электрический привод" + }, + "pokemon_form_595": { + "EN": "Burn", + "FR": "Module Pyro", + "DE": "Flammenmodul", + "RU": "Огненный привод" + }, + "pokemon_form_596": { + "EN": "Chill", + "FR": "Module Cryo", + "DE": "Gefriermodul", + "RU": "Ледяной привод" + }, + "pokemon_form_597": { + "EN": "Douse", + "FR": "Module Aqua", + "DE": "Aquamodul", + "RU": "Водный привод" + }, + "pokemon_form_599": { + "EN": "Noevolve" + }, + "pokemon_form_601": { + "EN": "Noevolve" + }, + "pokemon_form_603": { + "EN": "Noevolve" + }, + "pokemon_form_604": { + "EN": "Noevolve" + }, + "pokemon_form_605": { + "EN": "Noevolve" + }, + "pokemon_form_606": { + "EN": "Noevolve" + }, + "pokemon_form_607": { + "EN": "Noevolve" + }, + "pokemon_form_608": { + "EN": "Noevolve" + }, + "pokemon_form_609": { + "EN": "Noevolve" + }, + "pokemon_form_894": { + "EN": "Fall 2019", + "FR": "Automne 2019", + "DE": "Herbst 2019" + }, + "pokemon_form_895": { + "EN": "Fall 2019", + "FR": "Automne 2019", + "DE": "Herbst 2019" + }, + "pokemon_form_896": { + "EN": "Fall 2019", + "FR": "Automne 2019", + "DE": "Herbst 2019" + }, + "pokemon_form_897": { + "EN": "Fall 2019", + "FR": "Automne 2019", + "DE": "Herbst 2019" + }, + "pokemon_form_901": { + "EN": "Vs 2019" + }, + "pokemon_form_944": { + "EN": "Galarian", + "DE": "Galar", + "RU": "Галарская форма" + }, + "pokemon_form_946": { + "EN": "Galarian", + "DE": "Galar", + "RU": "Галарская форма" + }, + "pokemon_form_948": { + "EN": "Galarian", + "DE": "Galar", + "RU": "Галарская форма" + }, + "pokemon_form_949": { + "EN": "Copy 2019", + "FR": "Clone 2019", + "RU": "Клон 2019" + }, + "pokemon_form_950": { + "EN": "Copy 2019", + "FR": "Clone 2019", + "RU": "Клон 2019" + }, + "pokemon_form_951": { + "EN": "Copy 2019", + "FR": "Clone 2019", + "RU": "Клон 2019" + }, + "pokemon_form_952": { + "EN": "Copy 2019", + "FR": "Clone 2019", + "RU": "Клон 2019" + }, + "pokemon_form_2327": { + "EN": "Spring 2020", + "FR": "Printemps 2020", + "DE": "Frühjahr 2020", + "RU": "Весна 2020" + }, + "pokemon_form_2328": { + "EN": "Spring 2020", + "FR": "Printemps 2020", + "DE": "Frühjahr 2020", + "RU": "Весна 2020" + }, + "pokemon_form_2329": { + "EN": "Spring 2020", + "FR": "Printemps 2020", + "DE": "Frühjahr 2020", + "RU": "Весна 2020" + }, + "pokemon_form_2330": { + "PT-BR": "Fêmea", + "EN": "Female", + "FR": "Femelle", + "DE": "Weiblich", + "IT": "Femmina", + "RU": "Женская форма", + "ES": "Hembra" + }, + "pokemon_form_2331": { + "PT-BR": "Fêmea", + "EN": "Female", + "FR": "Femelle", + "DE": "Weiblich", + "IT": "Femmina", + "RU": "Женская форма", + "ES": "Hembra" + }, + "pokemon_form_2332": { + "EN": "Costume 2020", + "DE": "Kostüm 2020", + "RU": "Костюм 2020" + }, + "pokemon_form_2333": { + "EN": "Costume 2020", + "DE": "Kostüm 2020", + "RU": "Костюм 2020" + }, + "pokemon_form_2334": { + "EN": "Costume 2020", + "DE": "Kostüm 2020", + "RU": "Костюм 2020" + }, + "pokemon_form_2335": { + "EN": "Galarian", + "DE": "Galar", + "RU": "Галарская форма" + }, + "pokemon_form_2336": { + "EN": "Galarian", + "DE": "Galar", + "RU": "Галарская форма" + }, + "pokemon_form_2337": { + "EN": "Galarian", + "DE": "Galar", + "RU": "Галарская форма" + }, + "pokemon_form_2338": { + "EN": "Galarian", + "DE": "Galar", + "RU": "Галарская форма" + }, + "pokemon_form_2339": { + "EN": "Galarian", + "DE": "Galar", + "RU": "Галарская форма" + }, + "pokemon_form_2340": { + "EN": "Galarian", + "DE": "Galar", + "RU": "Галарская форма" + }, + "pokemon_form_2341": { + "EN": "Galarian", + "DE": "Galar", + "RU": "Галарская форма" + }, + "pokemon_form_2342": { + "EN": "Galarian Standard", + "DE": "Galar Normal", + "RU": "Галарская форма" + }, + "pokemon_form_2343": { + "EN": "Galarian Zen", + "DE": "Galar Trance", + "RU": "Галарская форма Дзен" + }, + "pokemon_form_2344": { + "EN": "Galarian", + "DE": "Galar", + "RU": "Галарская форма" + }, + "pokemon_form_2345": { + "EN": "Galarian", + "DE": "Galar", + "RU": "Галарская форма" + }, + "pokemon_form_2463": { + "PT-BR": "Forma Grave", + "EN": "Low Key Form", + "DE": "Tief-Form", + "IT": "Forma Basso", + "RU": "Сдержанная форма", + "ES": "Forma Grave" + }, + "pokemon_form_2464": { + "PT-BR": "Forma Aguda", + "EN": "Amped Form", + "DE": "Hoch-Form", + "IT": "Forma Melodia", + "RU": "Восторженная форма", + "ES": "Forma Aguda" + }, + "pokemon_form_2477": { + "PT-BR": "Forma Falsificada", + "EN": "Phony Form", + "FR": "Forme Contrefaçon", + "DE": "Fälschungsform", + "IT": "Forma Contraffatta", + "RU": "Фальшивая форма", + "ES": "Forma Falsificada" + }, + "pokemon_form_2478": { + "PT-BR": "Forma Autêntica", + "EN": "Antique Form", + "FR": "Forme Authentique", + "DE": "Originalform", + "IT": "Forma Autentica", + "RU": "Антикварная форма", + "ES": "Forma Genuina" + }, + "pokemon_form_2480": { + "PT-BR": "Forma Falsificada", + "EN": "Phony Form", + "FR": "Forme Contrefaçon", + "DE": "Fälschungsform", + "IT": "Forma Contraffatta", + "RU": "Фальшивая форма", + "ES": "Forma Falsificada" + }, + "pokemon_form_2481": { + "PT-BR": "Forma Autêntica", + "EN": "Antique Form", + "FR": "Forme Authentique", + "DE": "Originalform", + "IT": "Forma Autentica", + "RU": "Антикварная форма", + "ES": "Forma Genuina" + }, + "pokemon_form_2540": { + "PT-BR": "Cara de Gelo", + "EN": "Ice Face", + "DE": "Tiefkühlkopf", + "IT": "Gelofaccia", + "RU": "Ледяное лицо", + "ES": "Cara de Hielo" + }, + "pokemon_form_2541": { + "PT-BR": "Cara Degelada", + "EN": "Noice Face", + "DE": "Wohlfühlkopf", + "IT": "Liquefaccia", + "RU": "Неледяное лицо", + "ES": "Cara Deshielo" + }, + "pokemon_form_2542": { + "PT-BR": "Macho", + "EN": "Male", + "FR": "Mâle", + "DE": "Männlich", + "IT": "Maschio", + "RU": "Мужская форма", + "ES": "Macho" + }, + "pokemon_form_2543": { + "PT-BR": "Fêmea", + "EN": "Female", + "FR": "Femelle", + "DE": "Weiblich", + "IT": "Femmina", + "RU": "Женская форма", + "ES": "Hembra" + }, + "pokemon_form_2544": { + "EN": "Full Belly", + "DE": "Pappsatt", + "RU": "Режим Полный Живот" + }, + "pokemon_form_2545": { + "EN": "Hangry", + "DE": "Kohldampf", + "RU": "Злоголодный режим" + }, + "pokemon_form_2576": { + "EN": "Crowned Sword", + "DE": "König des Schwertes", + "RU": "Коронованный Меч" + }, + "pokemon_form_2577": { + "EN": "Hero", + "DE": "Heldenhafter Krieger", + "RU": "Герой многих битв" + }, + "pokemon_form_2578": { + "EN": "Crowned Shield", + "DE": "König des Schildes", + "RU": "Коронованный Щит" + }, + "pokemon_form_2579": { + "EN": "Hero", + "DE": "Heldenhafter Krieger", + "RU": "Герой многих битв" + }, + "pokemon_form_2580": { + "EN": "Eternamax", + "DE": "Unendynamax", + "RU": "Этернамакс" + }, + "pokemon_form_2582": { + "EN": "Galarian", + "DE": "Galar", + "RU": "Галарская форма" + }, + "pokemon_form_2583": { + "EN": "Galarian", + "DE": "Galar", + "RU": "Галарская форма" + }, + "pokemon_form_2584": { + "EN": "Galarian", + "DE": "Galar", + "RU": "Галарская форма" + }, + "pokemon_form_2585": { + "EN": "Costume 2020", + "DE": "Kostüm 2020", + "RU": "Костюм 2020" + }, + "pokemon_form_2586": { + "EN": "Costume 2020", + "DE": "Kostüm 2020", + "RU": "Костюм 2020" + }, + "pokemon_form_2588": { + "PT-BR": "Fêmea", + "EN": "Female", + "FR": "Femelle", + "DE": "Weiblich", + "IT": "Femmina", + "RU": "Женская форма", + "ES": "Hembra" + }, + "pokemon_form_2590": { + "PT-BR": "Fêmea", + "EN": "Female", + "FR": "Femelle", + "DE": "Weiblich", + "IT": "Femmina", + "RU": "Женская форма", + "ES": "Hembra" + }, + "pokemon_form_2591": { + "EN": "Ten Percent", + "DE": "10%", + "RU": "10% форма" + }, + "pokemon_form_2592": { + "EN": "Fifty Percent", + "DE": "50%", + "RU": "50% форма" + }, + "pokemon_form_2593": { + "EN": "Complete", + "DE": "Optimum", + "RU": "Завершенная форма" + }, + "pokemon_form_2594": { + "EN": "Archipelago", + "DE": "Archipel", + "RU": "Островная форма" + }, + "pokemon_form_2595": { + "EN": "Continental", + "DE": "Kontinental", + "RU": "Континентальная форма" + }, + "pokemon_form_2596": { + "EN": "Elegant", + "DE": "Prunk", + "RU": "Элегантная форма" + }, + "pokemon_form_2597": { + "EN": "Fancy", + "RU": "Изысканная форма" + }, + "pokemon_form_2598": { + "EN": "Garden", + "DE": "Ziergarten", + "RU": "Садовая форма" + }, + "pokemon_form_2599": { + "EN": "High Plains", + "DE": "Dürre", + "RU": "Равнинная форма" + }, + "pokemon_form_2600": { + "EN": "Icy Snow", + "DE": "Frost", + "RU": "Ледяная форма" + }, + "pokemon_form_2601": { + "EN": "Jungle", + "DE": "Dschungel", + "RU": "Форма джунглей" + }, + "pokemon_form_2602": { + "EN": "Marine", + "DE": "Aquamarin", + "RU": "Морская форма" + }, + "pokemon_form_2603": { + "EN": "Meadow", + "DE": "Blumenmeer", + "RU": "Луговая форма" + }, + "pokemon_form_2604": { + "EN": "Modern", + "DE": "Innovation", + "RU": "Современная форма" + }, + "pokemon_form_2605": { + "EN": "Monsoon", + "DE": "Monsun", + "RU": "Муссонная форма" + }, + "pokemon_form_2606": { + "EN": "Ocean", + "DE": "Ozean", + "RU": "Океаническая форма" + }, + "pokemon_form_2607": { + "EN": "Pokeball", + "RU": "Форма покебола" + }, + "pokemon_form_2608": { + "EN": "Polar", + "DE": "Schneefeld", + "RU": "Полярная форма" + }, + "pokemon_form_2609": { + "EN": "River", + "DE": "Flussdelta", + "RU": "Речная форма" + }, + "pokemon_form_2610": { + "EN": "Sandstorm", + "DE": "Sand", + "RU": "Форма песчанной бури" + }, + "pokemon_form_2611": { + "EN": "Savanna", + "DE": "Savanne", + "RU": "Саванновая форма" + }, + "pokemon_form_2612": { + "PT-BR": "Ensolarada", + "EN": "Sunny", + "FR": "Sun", + "DE": "Sonne", + "IT": "Sole", + "RU": "Солнечная форма", + "ES": "Sol" + }, + "pokemon_form_2613": { + "EN": "Tundra", + "DE": "Flocke", + "RU": "Тундровая форма" + }, + "pokemon_form_2614": { + "EN": "Red Flower", + "FR": "Red", + "DE": "Rotblütler", + "RU": "Красная форма" + }, + "pokemon_form_2615": { + "EN": "Yellow Flower", + "FR": "Yellow", + "DE": "Gelbblütler", + "RU": "Желтая форма" + }, + "pokemon_form_2616": { + "EN": "Orange Flower", + "FR": "Orange", + "DE": "Orangeblütler", + "RU": "Oранжевая форма" + }, + "pokemon_form_2617": { + "EN": "Blue Flower", + "FR": "Blue", + "DE": "Blaublütler", + "RU": "Синяя форма" + }, + "pokemon_form_2618": { + "EN": "White Flower", + "FR": "White", + "DE": "Weißblütler", + "RU": "Белая форма" + }, + "pokemon_form_2619": { + "EN": "Red Flower", + "FR": "Red", + "DE": "Rotblütler", + "RU": "Красная форма" + }, + "pokemon_form_2620": { + "EN": "Yellow Flower", + "FR": "Yellow", + "DE": "Gelbblütler", + "RU": "Желтая форма" + }, + "pokemon_form_2621": { + "EN": "Orange Flower", + "FR": "Orange", + "DE": "Orangeblütler", + "RU": "Оранжевая форма" + }, + "pokemon_form_2622": { + "EN": "Blue Flower", + "FR": "Blue", + "DE": "Blaublütler", + "RU": "Синяя форма" + }, + "pokemon_form_2623": { + "EN": "White Flower", + "FR": "White", + "DE": "Weißblütler", + "RU": "Белая форма" + }, + "pokemon_form_2624": { + "EN": "Red Flower", + "FR": "Red", + "DE": "Rotblütler", + "RU": "Красная форма" + }, + "pokemon_form_2625": { + "EN": "Yellow Flower", + "FR": "Yellow", + "DE": "Gelbblütler", + "RU": "Желтая форма" + }, + "pokemon_form_2626": { + "EN": "Orange Flower", + "FR": "Orange", + "DE": "Orangeblütler", + "RU": "Оранжевая форма" + }, + "pokemon_form_2627": { + "EN": "Blue Flower", + "FR": "Blue", + "DE": "Blaublütler", + "RU": "Синяя форма" + }, + "pokemon_form_2628": { + "EN": "White Flower", + "FR": "White", + "DE": "Weißblütler", + "RU": "Белая форма" + }, + "pokemon_form_2629": { + "EN": "Natural", + "DE": "Zottelform", + "RU": "Исходная форма" + }, + "pokemon_form_2630": { + "EN": "Heart Trim", + "FR": "Heart", + "DE": "Herzchenschnitt", + "RU": "Сердечная форма" + }, + "pokemon_form_2631": { + "EN": "Star Trim", + "FR": "Star", + "DE": "Sternchenschnitt", + "RU": "Звездная форма" + }, + "pokemon_form_2632": { + "EN": "Diamond Trim", + "FR": "Diamond", + "DE": "Diamantenschnitt", + "RU": "Бриллиантовая форма" + }, + "pokemon_form_2633": { + "EN": "Debutante Trim", + "FR": "Debutante", + "DE": "Fräuleinschnitt", + "RU": "Дебютантская форма" + }, + "pokemon_form_2634": { + "EN": "Matron Trim", + "FR": "Matron", + "DE": "Damenschnitt", + "RU": "Матронская форма" + }, + "pokemon_form_2635": { + "EN": "Dandy Trim", + "FR": "Dandy", + "DE": "Kavaliersschnitt", + "RU": "Светская форма" + }, + "pokemon_form_2636": { + "EN": "La Reine Trim", + "FR": "La Reine", + "DE": "Königinnenschnitt", + "RU": "Форма Ла Рейне" + }, + "pokemon_form_2637": { + "EN": "Kabuki Trim", + "FR": "Kabuki", + "DE": "Kabuki-Schnitt", + "RU": "Форма кабуки" + }, + "pokemon_form_2638": { + "EN": "Pharaoh Trim", + "FR": "Pharaoh", + "DE": "Herrscherschnitt", + "RU": "Форма фараона" + }, + "pokemon_form_2639": { + "EN": "Shield", + "DE": "Schild", + "RU": "Форма щита" + }, + "pokemon_form_2640": { + "EN": "Blade", + "DE": "Schwert", + "RU": "Форма меча" + }, + "pokemon_form_2641": { + "EN": "Small", + "FR": "Mini", + "DE": "S", + "RU": "Маленькая форма" + }, + "pokemon_form_2642": { + "EN": "Average", + "FR": "Normale", + "DE": "M", + "RU": "Средняя форма" + }, + "pokemon_form_2643": { + "EN": "Large", + "FR": "Maxi", + "DE": "L", + "RU": "Большая форма" + }, + "pokemon_form_2644": { + "EN": "Super", + "FR": "Ultra", + "DE": "XL", + "RU": "Огромная форма" + }, + "pokemon_form_2645": { + "EN": "Small", + "FR": "Mini", + "DE": "S", + "RU": "Маленькая форма" + }, + "pokemon_form_2646": { + "EN": "Average", + "FR": "Normale", + "DE": "M", + "RU": "Средняя форма" + }, + "pokemon_form_2647": { + "EN": "Large", + "FR": "Maxi", + "DE": "L", + "RU": "Большая форма" + }, + "pokemon_form_2648": { + "EN": "Super", + "FR": "Ultra", + "DE": "XL", + "RU": "Огромная форма" + }, + "pokemon_form_2649": { + "EN": "Neutral", + "RU": "Нейтральная форма" + }, + "pokemon_form_2650": { + "EN": "Active", + "DE": "Aktiv", + "RU": "Активная форма" + }, + "pokemon_form_2651": { + "EN": "Confined", + "DE": "Beschränkt", + "RU": "Невольная форма" + }, + "pokemon_form_2652": { + "EN": "Unbound", + "DE": "Ungebunden", + "RU": "Высвобожденная форма" + }, + "pokemon_form_2666": { + "EN": "Costume 2020 Deprecated", + "DE": "Kostüm 2020 Deprecated", + "RU": "Костюм 2020 Устаревший" + }, + "pokemon_form_2668": { + "EN": "Costume 2020", + "DE": "Kostüm 2020", + "RU": "Костюм 2020" + }, + "pokemon_form_2669": { + "EN": "Adventure Hat 2020", + "DE": "Abenteuerhut 2020" + }, + "pokemon_form_2670": { + "EN": "Winter 2020", + "FR": "Hiver 2020", + "RU": "Зима 2020" + }, + "pokemon_form_2671": { + "EN": "Winter 2020", + "FR": "Hiver 2020", + "RU": "Зима 2020" + }, + "pokemon_form_2672": { + "EN": "Winter 2020", + "FR": "Hiver 2020", + "RU": "Зима 2020" + }, + "pokemon_form_2673": { + "EN": "2020" + }, + "pokemon_form_2674": { + "EN": "2021" + }, + "pokemon_form_2675": { + "EN": "Kariyushi", + "RU": "Кариюши" + }, + "pokemon_form_2676": { + "EN": "Pop Star", + "RU": "Поп-звезда" + }, + "pokemon_form_2677": { + "EN": "Rock Star", + "RU": "Рок-звезда" + }, + "pokemon_form_2678": { + "EN": "Flying 5th Anniv", + "DE": "Fliegendes 5. Jubiläum", + "RU": "Пятилетняя годовщина" + }, + "pokemon_form_2679": { + "PT-BR": "Estilo Flamenco", + "EN": "Baile Style", + "FR": "Style Flamenco", + "DE": "Flamenco", + "IT": "Stile Flamenco", + "RU": "Бальный стиль", + "ES": "Estilo Apasionado" + }, + "pokemon_form_2680": { + "PT-BR": "Estilo Animado", + "EN": "Pom-Pom Style", + "FR": "Style Pom-Pom", + "DE": "Cheerleading", + "IT": "Stile Cheerdance", + "RU": "Стиль Пом-Пом", + "ES": "Estilo Animado" + }, + "pokemon_form_2681": { + "PT-BR": "Estilo Hula", + "EN": "Pa’u Style", + "FR": "Style Hula", + "DE": "Hula", + "IT": "Stile Hula", + "RU": "Стиль Пау", + "ES": "Estilo Plácido" + }, + "pokemon_form_2683": { + "PT-BR": "Estilo Elegante", + "EN": "Sensu Style", + "FR": "Style Buyô", + "DE": "Buyo", + "IT": "Stile Buyō", + "RU": "Чувственный стиль", + "ES": "Estilo Refinado" + }, + "pokemon_form_2684": { + "PT-BR": "Forma Diurna", + "EN": "Midday Form", + "FR": "Forme Diurne", + "DE": "Tag", + "IT": "Forma Giorno", + "RU": "Полуденная форма", + "ES": "Forma Diurna" + }, + "pokemon_form_2685": { + "PT-BR": "Forma Noturna", + "EN": "Midnight Form", + "FR": "Forme Nocturne", + "DE": "Nacht", + "IT": "Forma Notte", + "RU": "Полуночная форма", + "ES": "Forma Nocturna" + }, + "pokemon_form_2686": { + "PT-BR": "Forma Crepúsculo", + "EN": "Dusk Form", + "FR": "Forme Crépusculaire", + "DE": "Zwielicht", + "IT": "Forma Crepuscolo", + "RU": "Сумеречная форма", + "ES": "Forma Crepuscular" + }, + "pokemon_form_2687": { + "PT-BR": "Forma Individual", + "EN": "Solo Form", + "DE": "Einzel", + "IT": "Forma Individuale", + "RU": "Форма соло", + "ES": "Forma Individual" + }, + "pokemon_form_2688": { + "PT-BR": "Forma Cardume", + "EN": "School Form", + "DE": "Schwarm", + "IT": "Forma Banco", + "RU": "Форма стая", + "ES": "Forma Banco" + }, + "pokemon_form_2690": { + "PT-BR": "Inseto", + "EN": "Bug", + "FR": "Insecte", + "DE": "Käfer", + "IT": "Coleottero", + "RU": "Жучья форма", + "ES": "Bicho" + }, + "pokemon_form_2691": { + "PT-BR": "Sombrio", + "EN": "Dark", + "FR": "Ténèbres", + "DE": "Unlicht", + "IT": "Buio", + "RU": "Темная форма", + "ES": "Siniestro" + }, + "pokemon_form_2692": { + "PT-BR": "Dragão", + "EN": "Dragon", + "DE": "Drache", + "IT": "Drago", + "RU": "Драконья форма", + "ES": "Dragón" + }, + "pokemon_form_2693": { + "PT-BR": "Elétrico", + "EN": "Electric", + "FR": "Électrik", + "DE": "Elektro", + "IT": "Elettro", + "RU": "Электрическая форма", + "ES": "Eléctrico" + }, + "pokemon_form_2694": { + "PT-BR": "Fada", + "EN": "Fairy", + "FR": "Fée", + "DE": "Fee", + "IT": "Folletto", + "RU": "Феечная форма", + "ES": "Hada" + }, + "pokemon_form_2695": { + "PT-BR": "Lutador", + "EN": "Fighting", + "FR": "Combat", + "DE": "Kampf", + "IT": "Lotta", + "RU": "Боевая форма", + "ES": "Lucha" + }, + "pokemon_form_2696": { + "PT-BR": "Fogo", + "EN": "Fire", + "FR": "Feu", + "DE": "Feuer", + "IT": "Fuoco", + "RU": "Огненная форма", + "ES": "Fuego" + }, + "pokemon_form_2697": { + "PT-BR": "Voador", + "EN": "Flying", + "FR": "Vol", + "DE": "Flug", + "IT": "Volante", + "RU": "Летающая форма", + "ES": "Volador" + }, + "pokemon_form_2698": { + "PT-BR": "Fantasma", + "EN": "Ghost", + "FR": "Spectre", + "DE": "Geist", + "IT": "Spettro", + "RU": "Призрачная форма", + "ES": "Fantasma" + }, + "pokemon_form_2699": { + "PT-BR": "Planta", + "EN": "Grass", + "FR": "Plante", + "DE": "Pflanze", + "IT": "Erba", + "RU": "Травяная форма", + "ES": "Planta" + }, + "pokemon_form_2700": { + "PT-BR": "Terrestre", + "EN": "Ground", + "FR": "Sol", + "DE": "Boden", + "IT": "Terra", + "RU": "Земляная форма", + "ES": "Tierra" + }, + "pokemon_form_2701": { + "PT-BR": "Gelo", + "EN": "Ice", + "FR": "Glace", + "DE": "Eis", + "IT": "Ghiaccio", + "RU": "Ледяная форма", + "ES": "Hielo" + }, + "pokemon_form_2702": { + "PT-BR": "Venenoso", + "EN": "Poison", + "DE": "Gift", + "IT": "Veleno", + "RU": "Ядовитая форма", + "ES": "Veneno" + }, + "pokemon_form_2703": { + "PT-BR": "Psíquico", + "EN": "Psychic", + "FR": "Psy", + "DE": "Psycho", + "IT": "Psico", + "RU": "Психическая форма", + "ES": "Psíquico" + }, + "pokemon_form_2704": { + "PT-BR": "Pedra", + "EN": "Rock", + "FR": "Roche", + "DE": "Gestein", + "IT": "Roccia", + "RU": "Каменная форма", + "ES": "Roca" + }, + "pokemon_form_2705": { + "PT-BR": "Aço", + "EN": "Steel", + "FR": "Acier", + "DE": "Stahl", + "IT": "Acciaio", + "RU": "Стальная форма", + "ES": "Acero" + }, + "pokemon_form_2706": { + "PT-BR": "Água", + "EN": "Water", + "FR": "Eau", + "DE": "Wasser", + "IT": "Acqua", + "RU": "Водная форма", + "ES": "Agua" + }, + "pokemon_form_2707": { + "EN": "Meteor Blue", + "DE": "Meteor", + "RU": "Метеорная форма" + }, + "pokemon_form_2708": { + "PT-BR": "Plumagem Azul", + "EN": "Blue Plumage", + "FR": "Plumage Bleu", + "DE": "Blau", + "IT": "Piume Azzurre", + "RU": "Голубая форма", + "ES": "Plumaje Azul" + }, + "pokemon_form_2709": { + "PT-BR": "Plumagem Verde", + "EN": "Green Plumage", + "FR": "Plumage Vert", + "DE": "Grün", + "IT": "Piume Verdi", + "RU": "Зеленая форма", + "ES": "Plumaje Verde" + }, + "pokemon_form_2710": { + "EN": "Indigo", + "DE": "Hellblau", + "RU": "Индиго форма" + }, + "pokemon_form_2711": { + "EN": "Orange", + "RU": "Оранжевая форма" + }, + "pokemon_form_2712": { + "EN": "Red", + "DE": "Rot", + "RU": "Красная форма" + }, + "pokemon_form_2713": { + "EN": "Violet", + "DE": "Violett", + "RU": "Фиолетовая форма" + }, + "pokemon_form_2714": { + "PT-BR": "Plumagem Amarela", + "EN": "Yellow Plumage", + "FR": "Plumage Jaune", + "DE": "Gelb", + "IT": "Piume Gialle", + "RU": "Желтая форма", + "ES": "Plumaje Amarillo" + }, + "pokemon_form_2715": { + "PT-BR": "Forma Desmascarada", + "EN": "Busted Form", + "DE": "Entlarvt", + "IT": "Forma Smascherata", + "RU": "Сломанная форма", + "ES": "Forma Descubierta" + }, + "pokemon_form_2716": { + "PT-BR": "Forma Mascarada", + "EN": "Disguised Form", + "DE": "Standard", + "IT": "Forma Mascherata", + "RU": "Замаскированная форма", + "ES": "Forma Encubierta" + }, + "pokemon_form_2718": { + "EN": "Dusk Mane", + "DE": "Abendmähne", + "RU": "Грива заката" + }, + "pokemon_form_2719": { + "EN": "Dawn Wings", + "DE": "Morgenschwingen", + "RU": "Крылья рассвета" + }, + "pokemon_form_2720": { + "EN": "Ultra", + "RU": "Ультра форма" + }, + "pokemon_form_2722": { + "EN": "Original Color", + "DE": "Originalfarbe", + "RU": "Исходный цвет" + }, + "pokemon_form_2723": { + "PT-BR": "Estilo Golpe Decisivo", + "EN": "Single Strike Style", + "FR": "Style Poing Final", + "DE": "Fokussierter Stil", + "IT": "Stile Singolcolpo", + "RU": "Фома одного удара", + "ES": "Estilo Brusco" + }, + "pokemon_form_2724": { + "PT-BR": "Estilo Golpe Fluido", + "EN": "Rapid Strike Style", + "FR": "Style Mille Poings", + "DE": "Fließender Stil", + "IT": "Stile Pluricolpo", + "RU": "Форма множества ударов", + "ES": "Estilo Fluido" + }, + "pokemon_form_2726": { + "EN": "Ice Rider", + "DE": "Schimmelreiter", + "RU": "Ледяной всадник" + }, + "pokemon_form_2727": { + "EN": "Shadow Rider", + "RU": "Теневой всадник" + }, + "pokemon_form_2728": { + "EN": "Hisuian" + }, + "pokemon_form_2729": { + "EN": "S" + }, + "pokemon_form_2730": { + "EN": "S" + }, + "pokemon_form_2731": { + "EN": "S" + }, + "pokemon_form_2732": { + "EN": "S" + }, + "pokemon_form_2733": { + "EN": "S" + }, + "pokemon_form_2734": { + "EN": "2022" + }, + "pokemon_form_2735": { + "EN": "Hisuian" + }, + "pokemon_form_2736": { + "EN": "Flying Okinawa" + }, + "pokemon_form_2737": { + "PT-BR": "Forma Crepúsculo", + "EN": "Dusk Form", + "FR": "Forme Crépusculaire", + "DE": "Zwielichtform", + "IT": "Forma Crepuscolo", + "RU": "Сумеречная Форма", + "ES": "Forma Crepuscular" + }, + "pokemon_form_2739": { + "EN": "Meteor Green" + }, + "pokemon_form_2740": { + "EN": "Meteor Indigo" + }, + "pokemon_form_2741": { + "EN": "Meteor Orange" + }, + "pokemon_form_2742": { + "EN": "Meteor Red" + }, + "pokemon_form_2743": { + "EN": "Meteor Violet" + }, + "pokemon_form_2744": { + "EN": "Meteor Yellow" + }, + "pokemon_form_2745": { + "EN": "Archipelago" + }, + "pokemon_form_2746": { + "EN": "Continental" + }, + "pokemon_form_2747": { + "EN": "Elegant" + }, + "pokemon_form_2748": { + "EN": "Fancy" + }, + "pokemon_form_2749": { + "EN": "Garden" + }, + "pokemon_form_2750": { + "EN": "High Plains" + }, + "pokemon_form_2751": { + "EN": "Icy Snow" + }, + "pokemon_form_2752": { + "EN": "Jungle" + }, + "pokemon_form_2753": { + "EN": "Marine" + }, + "pokemon_form_2754": { + "EN": "Meadow" + }, + "pokemon_form_2755": { + "EN": "Modern" + }, + "pokemon_form_2756": { + "EN": "Monsoon" + }, + "pokemon_form_2757": { + "EN": "Ocean" + }, + "pokemon_form_2758": { + "EN": "Pokeball" + }, + "pokemon_form_2759": { + "EN": "Polar" + }, + "pokemon_form_2760": { + "EN": "River" + }, + "pokemon_form_2761": { + "EN": "Sandstorm" + }, + "pokemon_form_2762": { + "EN": "Savanna" + }, + "pokemon_form_2763": { + "PT-BR": "Ensolarada", + "EN": "Sunny", + "FR": "Solaire", + "DE": "Sonnenform", + "IT": "Sole", + "RU": "Солнечная Форма", + "ES": "Sol" + }, + "pokemon_form_2764": { + "EN": "Tundra" + }, + "pokemon_form_2765": { + "EN": "Archipelago" + }, + "pokemon_form_2766": { + "EN": "Continental" + }, + "pokemon_form_2767": { + "EN": "Elegant" + }, + "pokemon_form_2768": { + "EN": "Fancy" + }, + "pokemon_form_2769": { + "EN": "Garden" + }, + "pokemon_form_2770": { + "EN": "High Plains" + }, + "pokemon_form_2771": { + "EN": "Icy Snow" + }, + "pokemon_form_2772": { + "EN": "Jungle" + }, + "pokemon_form_2773": { + "EN": "Marine" + }, + "pokemon_form_2774": { + "EN": "Meadow" + }, + "pokemon_form_2775": { + "EN": "Modern" + }, + "pokemon_form_2776": { + "EN": "Monsoon" + }, + "pokemon_form_2777": { + "EN": "Ocean" + }, + "pokemon_form_2778": { + "EN": "Pokeball" + }, + "pokemon_form_2779": { + "EN": "Polar" + }, + "pokemon_form_2780": { + "EN": "River" + }, + "pokemon_form_2781": { + "EN": "Sandstorm" + }, + "pokemon_form_2782": { + "EN": "Savanna" + }, + "pokemon_form_2783": { + "PT-BR": "Ensolarada", + "EN": "Sunny", + "FR": "Solaire", + "DE": "Sonnenform", + "IT": "Sole", + "RU": "Солнечная Форма", + "ES": "Sol" + }, + "pokemon_form_2784": { + "EN": "Tundra" + }, + "pokemon_form_2785": { + "EN": "Hisuian" + }, + "pokemon_form_2786": { + "EN": "Hisuian" + }, + "pokemon_form_2787": { + "EN": "Hisuian" + }, + "pokemon_form_2788": { + "EN": "Hisuian" + }, + "pokemon_form_2789": { + "EN": "Hisuian" + }, + "pokemon_form_2790": { + "EN": "Hisuian" + }, + "pokemon_form_2791": { + "EN": "Hisuian" + }, + "pokemon_form_2792": { + "EN": "Hisuian" + }, + "pokemon_form_2793": { + "EN": "Hisuian" + }, + "pokemon_form_2794": { + "EN": "Hisuian" + }, + "pokemon_form_2795": { + "EN": "Hisuian" + }, + "pokemon_form_2796": { + "EN": "Hisuian" + }, + "pokemon_form_2797": { + "EN": "Hisuian" + }, + "pokemon_form_2798": { + "EN": "Hisuian" + }, + "pokemon_form_2799": { + "EN": "Galarian" + }, + "pokemon_form_2800": { + "EN": "Galarian" + }, + "pokemon_form_2801": { + "EN": "Galarian" + }, + "pokemon_form_2802": { + "PT-BR": "Forma Materializada", + "EN": "Incarnate Forme", + "FR": "Forme Avatar", + "DE": "Inkarnationsform", + "IT": "Forma Incarnazione", + "RU": "Воплощённая Форма", + "ES": "Forma Avatar" + }, + "pokemon_form_2803": { + "PT-BR": "Forma Therian", + "EN": "Therian Forme", + "FR": "Forme Totémique", + "DE": "Tiergeistform", + "IT": "Forma Totem", + "RU": "Териан-Форма", + "ES": "Forma Tótem" + }, + "pokemon_form_2804": { + "PT-BR": "Listras Brancas", + "EN": "White-Striped", + "FR": "Motif Blanc", + "DE": "Weißlinig", + "IT": "Forma Lineabianca", + "RU": "Белая Полосатая Форма", + "ES": "Raya Blanca" + }, + "pokemon_form_2805": { + "EN": "Gofest 2022" + }, + "pokemon_form_2806": { + "EN": "Wcs 2022" + }, + "pokemon_form_2808": { + "PT-BR": "Fêmea", + "EN": "Female", + "FR": "Femelle", + "DE": "Weiblich", + "IT": "Femmina", + "RU": "Женская Особь", + "ES": "Hembra" + }, + "pokemon_form_2813": { + "EN": "Tshirt 01" + }, + "pokemon_form_2814": { + "EN": "Tshirt 02" + }, + "pokemon_form_2815": { + "EN": "Flying 01" + }, + "pokemon_form_2816": { + "EN": "Flying 02" + }, + "pokemon_form_2820": { + "EN": "Winter 2020" + }, + "pokemon_form_2821": { + "EN": "S" + }, + "pokemon_form_2822": { + "EN": "S" + }, + "pokemon_form_2823": { + "EN": "Complete Ten Percent" + }, + "pokemon_form_2824": { + "EN": "Complete Fifty Percent" + }, + "pokemon_form_2825": { + "EN": "Gotour 2024 A" + }, + "pokemon_form_2826": { + "EN": "Gotour 2024 B" + }, + "pokemon_form_2827": { + "EN": "Gotour 2024 A 02" + }, + "pokemon_form_2828": { + "EN": "Gotour 2024 B 02" + }, + "pokemon_form_2829": { + "PT-BR": "Forma Origem", + "EN": "Origin Forme", + "FR": "Forme Originelle", + "DE": "Urform", + "IT": "Forma Originale", + "RU": "Исходная Форма", + "ES": "Forma Origen" + }, + "pokemon_form_2830": { + "PT-BR": "Forma Origem", + "EN": "Origin Forme", + "FR": "Forme Originelle", + "DE": "Urform", + "IT": "Forma Originale", + "RU": "Исходная Форма", + "ES": "Forma Origen" + }, + "pokemon_form_2832": { + "EN": "Tshirt 03" + }, + "pokemon_form_2833": { + "EN": "Flying 04" + }, + "pokemon_form_2834": { + "EN": "Tshirt 04" + }, + "pokemon_form_2835": { + "EN": "Tshirt 05" + }, + "pokemon_form_2836": { + "EN": "Tshirt 06" + }, + "pokemon_form_2837": { + "EN": "Tshirt 07" + }, + "pokemon_form_2838": { + "EN": "Flying 05" + }, + "pokemon_form_2839": { + "EN": "Flying 06" + }, + "pokemon_form_2840": { + "EN": "Flying 07" + }, + "pokemon_form_2841": { + "EN": "Flying 08" + }, + "pokemon_form_2842": { + "EN": "Horizons" + }, + "pokemon_form_2843": { + "EN": "Gofest 2024 Stiara" + }, + "pokemon_form_2844": { + "EN": "Gofest 2024 Mtiara" + }, + "pokemon_form_2845": { + "EN": "Gofest 2024 Stiara" + }, + "pokemon_form_2846": { + "EN": "Gofest 2024 Mtiara" + }, + "pokemon_form_2847": { + "EN": "Gofest 2024 Sscarf" + }, + "pokemon_form_2848": { + "EN": "Gofest 2024 Mscarf" + }, + "pokemon_form_2849": { + "EN": "Wildarea 2024" + }, + "pokemon_form_2850": { + "EN": "Diwali 2024" + }, + "pokemon_form_2856": { + "EN": "Gotour 2025 A" + }, + "pokemon_form_2857": { + "EN": "Gotour 2025 B" + }, + "pokemon_form_2858": { + "EN": "Gotour 2025 A 02" + }, + "pokemon_form_2859": { + "EN": "Gotour 2025 B 02" + }, + "pokemon_form_2863": { + "EN": "Kurta" + }, + "pokemon_form_2864": { + "EN": "Gofest 2025 Goggles Red" + }, + "pokemon_form_2865": { + "EN": "Gofest 2025 Goggles Blue" + }, + "pokemon_form_2866": { + "EN": "Gofest 2025 Goggles Yellow" + }, + "pokemon_form_2867": { + "EN": "Gofest 2025 Monocle Red" + }, + "pokemon_form_2868": { + "EN": "Gofest 2025 Monocle Blue" + }, + "pokemon_form_2869": { + "EN": "Gofest 2025 Monocle Yellow" + }, + "pokemon_form_2870": { + "EN": "Gofest 2025 Train Conductor" + }, + "pokemon_form_2871": { + "EN": "Counterfeit" + }, + "pokemon_form_2872": { + "EN": "Artisan" + }, + "pokemon_form_2873": { + "EN": "Unremarkable" + }, + "pokemon_form_2874": { + "EN": "Masterpiece" + }, + "pokemon_form_2875": { + "EN": "Teal" + }, + "pokemon_form_2876": { + "EN": "Wellspring" + }, + "pokemon_form_2877": { + "EN": "Hearthflame" + }, + "pokemon_form_2879": { + "EN": "Cornerstone" + }, + "pokemon_form_2881": { + "EN": "Terastal" + }, + "pokemon_form_2882": { + "EN": "Stellar" + }, + "pokemon_form_2982": { + "PT-BR": "Fêmea", + "EN": "Female", + "FR": "Femelle", + "DE": "Weiblich", + "IT": "Femmina", + "RU": "Женская Особь", + "ES": "Hembra" + }, + "pokemon_form_2983": { + "EN": "Family Of Three" + }, + "pokemon_form_2984": { + "EN": "Family Of Four" + }, + "pokemon_form_2985": { + "PT-BR": "Plumagem Verde", + "EN": "Green Plumage", + "FR": "Plumage Vert", + "DE": "Grüngefiedert", + "IT": "Piume Verdi", + "RU": "Зелёное Оперение", + "ES": "Plumaje Verde" + }, + "pokemon_form_2986": { + "PT-BR": "Plumagem Azul", + "EN": "Blue Plumage", + "FR": "Plumage Bleu", + "DE": "Blaugefiedert", + "IT": "Piume Azzurre", + "RU": "Синее Оперение", + "ES": "Plumaje Azul" + }, + "pokemon_form_2987": { + "PT-BR": "Plumagem Amarela", + "EN": "Yellow Plumage", + "FR": "Plumage Jaune", + "DE": "Gelbgefiedert", + "IT": "Piume Gialle", + "RU": "Жёлтое Оперение", + "ES": "Plumaje Amarillo" + }, + "pokemon_form_2988": { + "EN": "White" + }, + "pokemon_form_2989": { + "PT-BR": "Forma Zero", + "EN": "Zero Form", + "FR": "Forme Ordinaire", + "DE": "Alltagsform", + "IT": "Forma Ingenua", + "RU": "Нулевая Форма", + "ES": "Forma Ingenua" + }, + "pokemon_form_2990": { + "EN": "Hero" + }, + "pokemon_form_2991": { + "PT-BR": "Forma Curvada", + "EN": "Curly Form", + "FR": "Forme Courbée", + "DE": "Gebogene Form", + "IT": "Forma Arcuata", + "RU": "Изогнутая Форма", + "ES": "Forma Curvada" + }, + "pokemon_form_2992": { + "PT-BR": "Forma Pendular", + "EN": "Droopy Form", + "FR": "Forme Affalée", + "DE": "Hängende Form", + "IT": "Forma Adagiata", + "RU": "Свисающая Форма", + "ES": "Forma Lánguida" + }, + "pokemon_form_2993": { + "PT-BR": "Forma Estendida", + "EN": "Stretchy Form", + "FR": "Forme Raide", + "DE": "Gestreckte Form", + "IT": "Forma Tesa", + "RU": "Вытянутая Форма", + "ES": "Forma Recta" + }, + "pokemon_form_2994": { + "PT-BR": "Forma Bissegmentar", + "EN": "Two-Segment Form", + "FR": "Forme Double", + "DE": "Zweisegmentform", + "IT": "Forma Bimetamero", + "RU": "Двухсегментная Форма", + "ES": "Forma Binodular" + }, + "pokemon_form_2995": { + "PT-BR": "Forma Trissegmentar", + "EN": "Three-Segment Form", + "FR": "Forme Triple", + "DE": "Dreisegmentform", + "IT": "Forma Trimetamero", + "RU": "Трёхсегментная Форма", + "ES": "Forma Trinodular" + }, + "pokemon_form_2996": { + "PT-BR": "Versão Plena", + "EN": "Apex Build", + "FR": "Forme Finale", + "DE": "Vollkommene Gestalt", + "IT": "Foggia Integrale", + "RU": "Финальная Форма", + "ES": "Fisonomía Plena" + }, + "pokemon_form_2997": { + "PT-BR": "Modo Supremo", + "EN": "Ultimate Mode", + "FR": "Mode Ultime", + "DE": "Kompletter Modus", + "IT": "Assetto Completo", + "RU": "Полноценный Режим", + "ES": "Modo Pleno" + }, + "pokemon_form_3001": { + "EN": "Summer 2023" + }, + "pokemon_form_3002": { + "EN": "Summer 2023 A" + }, + "pokemon_form_3003": { + "EN": "Summer 2023 B" + }, + "pokemon_form_3004": { + "EN": "Summer 2023 C" + }, + "pokemon_form_3005": { + "EN": "Summer 2023 D" + }, + "pokemon_form_3006": { + "PT-BR": "Espécie de Combate", + "EN": "Combat Breed", + "FR": "Race Combative", + "DE": "Gefechtvariante", + "IT": "Varietà Combattiva", + "RU": "Порода Сражение", + "ES": "Variedad Combatiente" + }, + "pokemon_form_3007": { + "PT-BR": "Espécie Labareda", + "EN": "Blaze Breed", + "FR": "Race Flamboyante", + "DE": "Flammenvariante", + "IT": "Varietà Infuocata", + "RU": "Порода Пламя", + "ES": "Variedad Ardiente" + }, + "pokemon_form_3008": { + "PT-BR": "Espécie Aquática", + "EN": "Aqua Breed", + "FR": "Race Aquatique", + "DE": "Flutenvariante", + "IT": "Varietà Acquatica", + "RU": "Порода Аква", + "ES": "Variedad Acuática" + }, + "pokemon_form_3009": { + "EN": "Paldea" + }, + "pokemon_form_3010": { + "EN": "Summer 2023 E" + }, + "pokemon_form_3011": { + "EN": "Flying 03" + }, + "pokemon_form_3012": { + "EN": "Jeju" + }, + "pokemon_form_3013": { + "EN": "Doctor" + }, + "pokemon_form_3014": { + "EN": "Wcs 2023" + }, + "pokemon_form_3015": { + "EN": "Wcs 2024" + }, + "pokemon_form_3017": { + "EN": "Wcs 2025" + }, + "pokemon_form_3018": { + "EN": "Coin A1" + }, + "pokemon_form_3019": { + "EN": "Swim 2025" + }, + "pokemon_form_3021": { + "EN": "Wildarea 2025" + }, + "pokemon_form_3315": { + "EN": "Winter 2025" + }, + "pokemon_form_3316": { + "EN": "Winter 2025" + }, + "pokemon_form_3317": { + "EN": "Winter 2025" + }, + "pokemon_form_3318": { + "EN": "Gotour 2026 A" + }, + "pokemon_form_3319": { + "EN": "Gotour 2026 A 02" + }, + "pokemon_form_3320": { + "EN": "Gotour 2026 B" + }, + "pokemon_form_3321": { + "EN": "Gotour 2026 B 02" + }, + "pokemon_form_3322": { + "EN": "Gotour 2026 C" + }, + "pokemon_form_3323": { + "EN": "Gotour 2026 C 02" + }, + "pokemon_form_3337": { + "EN": "Spring 2026 A" + }, + "pokemon_form_3338": { + "EN": "Spring 2026 B" + }, + "pokemon_form_3339": { + "EN": "Spring 2026" + }, + "pokemon_form_3340": { + "EN": "Bb 2026" + }, + "pokemon_form_3341": { + "EN": "Visor 2026" + } +} \ No newline at end of file diff --git a/core/lang/pokemon_moves.json b/lang/pokemon_moves.json similarity index 67% rename from core/lang/pokemon_moves.json rename to lang/pokemon_moves.json index e9078e0a..2f0bafb6 100644 --- a/core/lang/pokemon_moves.json +++ b/lang/pokemon_moves.json @@ -2,10 +2,6 @@ "pokemon_move_1": { "PT-BR": "Trovoada de Choques", "EN": "Thunder Shock", - "FI": "Thunder Shock", - "NL": "Thunder Shock", - "NO": "Thunder Shock", - "PL": "Thunder Shock", "FR": "Éclair", "DE": "Donnerschock", "IT": "Tuonoshock", @@ -15,10 +11,6 @@ "pokemon_move_2": { "PT-BR": "Ataque Rápido", "EN": "Quick Attack", - "FI": "Quick Attack", - "NL": "Quick Attack", - "NO": "Quick Attack", - "PL": "Quick Attack", "FR": "Vive-Attaque", "DE": "Ruckzuckhieb", "IT": "Attacco Rapido", @@ -28,10 +20,6 @@ "pokemon_move_3": { "PT-BR": "Arranhão", "EN": "Scratch", - "FI": "Scratch", - "NL": "Scratch", - "NO": "Scratch", - "PL": "Scratch", "FR": "Griffe", "DE": "Kratzer", "IT": "Graffio", @@ -41,10 +29,6 @@ "pokemon_move_4": { "PT-BR": "Brasa", "EN": "Ember", - "FI": "Ember", - "NL": "Ember", - "NO": "Ember", - "PL": "Ember", "FR": "Flammèche", "DE": "Glut", "IT": "Braciere", @@ -54,25 +38,16 @@ "pokemon_move_5": { "PT-BR": "Chicote de Vinha", "EN": "Vine Whip", - "FI": "Vine Whip", - "NL": "Vine Whip", - "NO": "Vine Whip", - "PL": "Vine Whip", "FR": "Fouet Lianes", "DE": "Rankenhieb", "IT": "Frustata", - "RU": "Плеть-Лиана", + "RU": "Плеть-лиана", "ES": "Látigo Cepa" }, "pokemon_move_6": { "PT-BR": "Investida", "EN": "Tackle", - "FI": "Tackle", - "NL": "Tackle", - "NO": "Tackle", - "PL": "Tackle", "FR": "Charge", - "DE": "Tackle", "IT": "Azione", "RU": "Бросок", "ES": "Placaje" @@ -80,10 +55,6 @@ "pokemon_move_7": { "PT-BR": "Folha Navalha", "EN": "Razor Leaf", - "FI": "Razor Leaf", - "NL": "Razor Leaf", - "NO": "Razor Leaf", - "PL": "Razor Leaf", "FR": "Tranch’Herbe", "DE": "Rasierblatt", "IT": "Foglielama", @@ -93,10 +64,6 @@ "pokemon_move_8": { "PT-BR": "Desmantelar", "EN": "Take Down", - "FI": "Take Down", - "NL": "Take Down", - "NO": "Take Down", - "PL": "Take Down", "FR": "Bélier", "DE": "Bodycheck", "IT": "Riduttore", @@ -106,10 +73,6 @@ "pokemon_move_9": { "PT-BR": "Revólver d'Água", "EN": "Water Gun", - "FI": "Water Gun", - "NL": "Water Gun", - "NO": "Water Gun", - "PL": "Water Gun", "FR": "Pistolet à O", "DE": "Aquaknarre", "IT": "Pistolacqua", @@ -119,10 +82,6 @@ "pokemon_move_10": { "PT-BR": "Mordida", "EN": "Bite", - "FI": "Bite", - "NL": "Bite", - "NO": "Bite", - "PL": "Bite", "FR": "Morsure", "DE": "Biss", "IT": "Morso", @@ -132,10 +91,6 @@ "pokemon_move_11": { "PT-BR": "Pancada", "EN": "Pound", - "FI": "Pound", - "NL": "Pound", - "NO": "Pound", - "PL": "Pound", "FR": "Écras’Face", "DE": "Klaps", "IT": "Botta", @@ -145,10 +100,6 @@ "pokemon_move_12": { "PT-BR": "Tapa Duplo", "EN": "Double Slap", - "FI": "Double Slap", - "NL": "Double Slap", - "NO": "Double Slap", - "PL": "Double Slap", "FR": "Torgnoles", "DE": "Duplexhieb", "IT": "Doppiasberla", @@ -158,10 +109,6 @@ "pokemon_move_13": { "PT-BR": "Embrulho", "EN": "Wrap", - "FI": "Wrap", - "NL": "Wrap", - "NO": "Wrap", - "PL": "Wrap", "FR": "Ligotage", "DE": "Wickel", "IT": "Avvolgibotta", @@ -171,10 +118,6 @@ "pokemon_move_14": { "PT-BR": "Hiper-raio", "EN": "Hyper Beam", - "FI": "Hyper Beam", - "NL": "Hyper Beam", - "NO": "Hyper Beam", - "PL": "Hyper Beam", "FR": "Ultralaser", "DE": "Hyperstrahl", "IT": "Iper Raggio", @@ -184,10 +127,6 @@ "pokemon_move_15": { "PT-BR": "Lambida", "EN": "Lick", - "FI": "Lick", - "NL": "Lick", - "NO": "Lick", - "PL": "Lick", "FR": "Léchouille", "DE": "Schlecker", "IT": "Leccata", @@ -197,10 +136,6 @@ "pokemon_move_16": { "PT-BR": "Pulso Sombrio", "EN": "Dark Pulse", - "FI": "Dark Pulse", - "NL": "Dark Pulse", - "NO": "Dark Pulse", - "PL": "Dark Pulse", "FR": "Vibrobscur", "DE": "Finsteraura", "IT": "Neropulsar", @@ -210,23 +145,13 @@ "pokemon_move_17": { "PT-BR": "Nevoeiro de Fumaça", "EN": "Smog", - "FI": "Smog", - "NL": "Smog", - "NO": "Smog", - "PL": "Smog", "FR": "Purédpois", - "DE": "Smog", - "IT": "Smog", "RU": "Дым", "ES": "Polución" }, "pokemon_move_18": { "PT-BR": "Ataque de Lama", "EN": "Sludge", - "FI": "Sludge", - "NL": "Sludge", - "NO": "Sludge", - "PL": "Sludge", "FR": "Détritus", "DE": "Schlammbad", "IT": "Fango", @@ -236,10 +161,6 @@ "pokemon_move_19": { "PT-BR": "Garra de Metal", "EN": "Metal Claw", - "FI": "Metal Claw", - "NL": "Metal Claw", - "NO": "Metal Claw", - "PL": "Metal Claw", "FR": "Griffe Acier", "DE": "Metallklaue", "IT": "Ferrartigli", @@ -249,10 +170,6 @@ "pokemon_move_20": { "PT-BR": "Agarramento Compressor", "EN": "Vise Grip", - "FI": "Vise Grip", - "NL": "Vise Grip", - "NO": "Vise Grip", - "PL": "Vise Grip", "FR": "Force Poigne", "DE": "Klammer", "IT": "Presa", @@ -262,10 +179,6 @@ "pokemon_move_21": { "PT-BR": "Roda de Fogo", "EN": "Flame Wheel", - "FI": "Flame Wheel", - "NL": "Flame Wheel", - "NO": "Flame Wheel", - "PL": "Flame Wheel", "FR": "Roue de Feu", "DE": "Flammenrad", "IT": "Ruotafuoco", @@ -275,10 +188,6 @@ "pokemon_move_22": { "PT-BR": "Megachifre", "EN": "Megahorn", - "FI": "Megahorn", - "NL": "Megahorn", - "NO": "Megahorn", - "PL": "Megahorn", "FR": "Mégacorne", "DE": "Vielender", "IT": "Megacorno", @@ -288,10 +197,6 @@ "pokemon_move_23": { "PT-BR": "Ataque de Asa", "EN": "Wing Attack", - "FI": "Wing Attack", - "NL": "Wing Attack", - "NO": "Wing Attack", - "PL": "Wing Attack", "FR": "Cru-Ailes", "DE": "Flügelschlag", "IT": "Attacco d’Ala", @@ -301,10 +206,6 @@ "pokemon_move_24": { "PT-BR": "Lança-chamas", "EN": "Flamethrower", - "FI": "Flamethrower", - "NL": "Flamethrower", - "NO": "Flamethrower", - "PL": "Flamethrower", "FR": "Lance-Flammes", "DE": "Flammenwurf", "IT": "Lanciafiamme", @@ -314,10 +215,6 @@ "pokemon_move_25": { "PT-BR": "Soco Enganador", "EN": "Sucker Punch", - "FI": "Sucker Punch", - "NL": "Sucker Punch", - "NO": "Sucker Punch", - "PL": "Sucker Punch", "FR": "Coup Bas", "DE": "Tiefschlag", "IT": "Sbigoattacco", @@ -327,10 +224,6 @@ "pokemon_move_26": { "PT-BR": "Cavar", "EN": "Dig", - "FI": "Dig", - "NL": "Dig", - "NO": "Dig", - "PL": "Dig", "FR": "Tunnel", "DE": "Schaufler", "IT": "Fossa", @@ -340,10 +233,6 @@ "pokemon_move_27": { "PT-BR": "Rasteira", "EN": "Low Kick", - "FI": "Low Kick", - "NL": "Low Kick", - "NO": "Low Kick", - "PL": "Low Kick", "FR": "Balayage", "DE": "Fußkick", "IT": "Colpo Basso", @@ -353,10 +242,6 @@ "pokemon_move_28": { "PT-BR": "Golpe Cruzado", "EN": "Cross Chop", - "FI": "Cross Chop", - "NL": "Cross Chop", - "NO": "Cross Chop", - "PL": "Cross Chop", "FR": "Coup Croix", "DE": "Kreuzhieb", "IT": "Incrocolpo", @@ -366,10 +251,6 @@ "pokemon_move_29": { "PT-BR": "Corte Psíquico", "EN": "Psycho Cut", - "FI": "Psycho Cut", - "NL": "Psycho Cut", - "NO": "Psycho Cut", - "PL": "Psycho Cut", "FR": "Coupe Psycho", "DE": "Psychoklinge", "IT": "Psicotaglio", @@ -379,10 +260,6 @@ "pokemon_move_30": { "PT-BR": "Feixe Psíquico", "EN": "Psybeam", - "FI": "Psybeam", - "NL": "Psybeam", - "NO": "Psybeam", - "PL": "Psybeam", "FR": "Rafale Psy", "DE": "Psystrahl", "IT": "Psicoraggio", @@ -392,10 +269,6 @@ "pokemon_move_31": { "PT-BR": "Terremoto", "EN": "Earthquake", - "FI": "Earthquake", - "NL": "Earthquake", - "NO": "Earthquake", - "PL": "Earthquake", "FR": "Séisme", "DE": "Erdbeben", "IT": "Terremoto", @@ -405,10 +278,6 @@ "pokemon_move_32": { "PT-BR": "Gume de Pedra", "EN": "Stone Edge", - "FI": "Stone Edge", - "NL": "Stone Edge", - "NO": "Stone Edge", - "PL": "Stone Edge", "FR": "Lame de Roc", "DE": "Steinkante", "IT": "Pietrataglio", @@ -418,10 +287,6 @@ "pokemon_move_33": { "PT-BR": "Soco de Gelo", "EN": "Ice Punch", - "FI": "Ice Punch", - "NL": "Ice Punch", - "NO": "Ice Punch", - "PL": "Ice Punch", "FR": "Poing Glace", "DE": "Eishieb", "IT": "Gelopugno", @@ -431,23 +296,15 @@ "pokemon_move_34": { "PT-BR": "Estampa de Coração", "EN": "Heart Stamp", - "FI": "Heart Stamp", - "NL": "Heart Stamp", - "NO": "Heart Stamp", - "PL": "Heart Stamp", "FR": "Crève-Cœur", "DE": "Herzstempel", "IT": "Cuorestampo", - "RU": "Печать-Сердце", + "RU": "ПечатьСердце", "ES": "Arrumaco" }, "pokemon_move_35": { "PT-BR": "Descarga", "EN": "Discharge", - "FI": "Discharge", - "NL": "Discharge", - "NO": "Discharge", - "PL": "Discharge", "FR": "Coup d’Jus", "DE": "Ladungsstoß", "IT": "Scarica", @@ -457,10 +314,6 @@ "pokemon_move_36": { "PT-BR": "Canhão de Flash", "EN": "Flash Cannon", - "FI": "Flash Cannon", - "NL": "Flash Cannon", - "NO": "Flash Cannon", - "PL": "Flash Cannon", "FR": "Luminocanon", "DE": "Lichtkanone", "IT": "Cannonflash", @@ -470,10 +323,6 @@ "pokemon_move_37": { "PT-BR": "Bicada", "EN": "Peck", - "FI": "Peck", - "NL": "Peck", - "NO": "Peck", - "PL": "Peck", "FR": "Picpic", "DE": "Pikser", "IT": "Beccata", @@ -483,10 +332,6 @@ "pokemon_move_38": { "PT-BR": "Bico Broca", "EN": "Drill Peck", - "FI": "Drill Peck", - "NL": "Drill Peck", - "NO": "Drill Peck", - "PL": "Drill Peck", "FR": "Bec Vrille", "DE": "Bohrschnabel", "IT": "Perforbecco", @@ -496,10 +341,6 @@ "pokemon_move_39": { "PT-BR": "Raio Congelante", "EN": "Ice Beam", - "FI": "Ice Beam", - "NL": "Ice Beam", - "NO": "Ice Beam", - "PL": "Ice Beam", "FR": "Laser Glace", "DE": "Eisstrahl", "IT": "Geloraggio", @@ -509,12 +350,6 @@ "pokemon_move_40": { "PT-BR": "Nevasca", "EN": "Blizzard", - "FI": "Blizzard", - "NL": "Blizzard", - "NO": "Blizzard", - "PL": "Blizzard", - "FR": "Blizzard", - "DE": "Blizzard", "IT": "Bora", "RU": "Метель", "ES": "Ventisca" @@ -522,10 +357,6 @@ "pokemon_move_41": { "PT-BR": "Golpe de Ar", "EN": "Air Slash", - "FI": "Air Slash", - "NL": "Air Slash", - "NO": "Air Slash", - "PL": "Air Slash", "FR": "Lame d’Air", "DE": "Luftschnitt", "IT": "Eterelama", @@ -535,10 +366,6 @@ "pokemon_move_42": { "PT-BR": "Onda de Calor", "EN": "Heat Wave", - "FI": "Heat Wave", - "NL": "Heat Wave", - "NO": "Heat Wave", - "PL": "Heat Wave", "FR": "Canicule", "DE": "Hitzewelle", "IT": "Ondacalda", @@ -548,10 +375,6 @@ "pokemon_move_43": { "PT-BR": "Agulha Dupla", "EN": "Twineedle", - "FI": "Twineedle", - "NL": "Twineedle", - "NO": "Twineedle", - "PL": "Twineedle", "FR": "Double Dard", "DE": "Duonadel", "IT": "Doppio Ago", @@ -561,10 +384,6 @@ "pokemon_move_44": { "PT-BR": "Golpe Envenenado", "EN": "Poison Jab", - "FI": "Poison Jab", - "NL": "Poison Jab", - "NO": "Poison Jab", - "PL": "Poison Jab", "FR": "Direct Toxik", "DE": "Gifthieb", "IT": "Velenpuntura", @@ -574,10 +393,6 @@ "pokemon_move_45": { "PT-BR": "Ás dos Ares", "EN": "Aerial Ace", - "FI": "Aerial Ace", - "NL": "Aerial Ace", - "NO": "Aerial Ace", - "PL": "Aerial Ace", "FR": "Aéropique", "DE": "Aero-Ass", "IT": "Aeroassalto", @@ -587,10 +402,6 @@ "pokemon_move_46": { "PT-BR": "Furação", "EN": "Drill Run", - "FI": "Drill Run", - "NL": "Drill Run", - "NO": "Drill Run", - "PL": "Drill Run", "FR": "Tunnelier", "DE": "Schlagbohrer", "IT": "Giravvita", @@ -600,10 +411,6 @@ "pokemon_move_47": { "PT-BR": "Nevasca de Pétalas", "EN": "Petal Blizzard", - "FI": "Petal Blizzard", - "NL": "Petal Blizzard", - "NO": "Petal Blizzard", - "PL": "Petal Blizzard", "FR": "Tempête Florale", "DE": "Blütenwirbel", "IT": "Fiortempesta", @@ -613,23 +420,15 @@ "pokemon_move_48": { "PT-BR": "Megadreno", "EN": "Mega Drain", - "FI": "Mega Drain", - "NL": "Mega Drain", - "NO": "Mega Drain", - "PL": "Mega Drain", "FR": "Méga-Sangsue", "DE": "Megasauger", "IT": "Megassorbimento", - "RU": "Мега-Осушение", + "RU": "Мега-осушение", "ES": "Megaagotar" }, "pokemon_move_49": { "PT-BR": "Zumbido de Inseto", "EN": "Bug Buzz", - "FI": "Bug Buzz", - "NL": "Bug Buzz", - "NO": "Bug Buzz", - "PL": "Bug Buzz", "FR": "Bourdon", "DE": "Käfergebrumm", "IT": "Ronzio", @@ -639,10 +438,6 @@ "pokemon_move_50": { "PT-BR": "Presa Venenosa", "EN": "Poison Fang", - "FI": "Poison Fang", - "NL": "Poison Fang", - "NO": "Poison Fang", - "PL": "Poison Fang", "FR": "Crochet Venin", "DE": "Giftzahn", "IT": "Velenodenti", @@ -652,10 +447,6 @@ "pokemon_move_51": { "PT-BR": "Talho Noturno", "EN": "Night Slash", - "FI": "Night Slash", - "NL": "Night Slash", - "NO": "Night Slash", - "PL": "Night Slash", "FR": "Tranche-Nuit", "DE": "Nachthieb", "IT": "Nottesferza", @@ -665,10 +456,6 @@ "pokemon_move_52": { "PT-BR": "Talho", "EN": "Slash", - "FI": "Slash", - "NL": "Slash", - "NO": "Slash", - "PL": "Slash", "FR": "Tranche", "DE": "Schlitzer", "IT": "Lacerazione", @@ -678,10 +465,6 @@ "pokemon_move_53": { "PT-BR": "Jato de Bolhas", "EN": "Bubble Beam", - "FI": "Bubble Beam", - "NL": "Bubble Beam", - "NO": "Bubble Beam", - "PL": "Bubble Beam", "FR": "Bulles d’O", "DE": "Blubbstrahl", "IT": "Bollaraggio", @@ -691,10 +474,6 @@ "pokemon_move_54": { "PT-BR": "Submissão", "EN": "Submission", - "FI": "Submission", - "NL": "Submission", - "NO": "Submission", - "PL": "Submission", "FR": "Sacrifice", "DE": "Überroller", "IT": "Sottomissione", @@ -704,10 +483,6 @@ "pokemon_move_55": { "PT-BR": "Golpe de Caratê", "EN": "Karate Chop", - "FI": "Karate Chop", - "NL": "Karate Chop", - "NO": "Karate Chop", - "PL": "Karate Chop", "FR": "Poing Karaté", "DE": "Karateschlag", "IT": "Colpokarate", @@ -717,10 +492,6 @@ "pokemon_move_56": { "PT-BR": "Movimento Baixo", "EN": "Low Sweep", - "FI": "Low Sweep", - "NL": "Low Sweep", - "NO": "Low Sweep", - "PL": "Low Sweep", "FR": "Balayette", "DE": "Fußtritt", "IT": "Calciobasso", @@ -730,10 +501,6 @@ "pokemon_move_57": { "PT-BR": "Aqua Jato", "EN": "Aqua Jet", - "FI": "Aqua Jet", - "NL": "Aqua Jet", - "NO": "Aqua Jet", - "PL": "Aqua Jet", "FR": "Aqua-Jet", "DE": "Wasserdüse", "IT": "Acquagetto", @@ -743,10 +510,6 @@ "pokemon_move_58": { "PT-BR": "Aqua Cauda", "EN": "Aqua Tail", - "FI": "Aqua Tail", - "NL": "Aqua Tail", - "NO": "Aqua Tail", - "PL": "Aqua Tail", "FR": "Hydro-Queue", "DE": "Nassschweif", "IT": "Idrondata", @@ -756,23 +519,15 @@ "pokemon_move_59": { "PT-BR": "Bomba de Sementes", "EN": "Seed Bomb", - "FI": "Seed Bomb", - "NL": "Seed Bomb", - "NO": "Seed Bomb", - "PL": "Seed Bomb", "FR": "Canon Graine", "DE": "Samenbomben", "IT": "Semebomba", - "RU": "Семя-Бомба", + "RU": "Семя-бомба", "ES": "Bomba Germen" }, "pokemon_move_60": { "PT-BR": "Choque Psíquico", "EN": "Psyshock", - "FI": "Psyshock", - "NL": "Psyshock", - "NO": "Psyshock", - "PL": "Psyshock", "FR": "Choc Psy", "DE": "Psychoschock", "IT": "Psicoshock", @@ -782,10 +537,6 @@ "pokemon_move_61": { "PT-BR": "Lançamento de Rocha", "EN": "Rock Throw", - "FI": "Rock Throw", - "NL": "Rock Throw", - "NO": "Rock Throw", - "PL": "Rock Throw", "FR": "Jet-Pierres", "DE": "Steinwurf", "IT": "Sassata", @@ -795,10 +546,6 @@ "pokemon_move_62": { "PT-BR": "Poder Ancestral", "EN": "Ancient Power", - "FI": "Ancient Power", - "NL": "Ancient Power", - "NO": "Ancient Power", - "PL": "Ancient Power", "FR": "Pouvoir Antique", "DE": "Antik-Kraft", "IT": "Forzantica", @@ -808,10 +555,6 @@ "pokemon_move_63": { "PT-BR": "Tumba de Rochas", "EN": "Rock Tomb", - "FI": "Rock Tomb", - "NL": "Rock Tomb", - "NO": "Rock Tomb", - "PL": "Rock Tomb", "FR": "Tomberoche", "DE": "Felsgrab", "IT": "Rocciotomba", @@ -821,10 +564,6 @@ "pokemon_move_64": { "PT-BR": "Deslize de Pedras", "EN": "Rock Slide", - "FI": "Rock Slide", - "NL": "Rock Slide", - "NO": "Rock Slide", - "PL": "Rock Slide", "FR": "Éboulement", "DE": "Steinhagel", "IT": "Frana", @@ -834,10 +573,6 @@ "pokemon_move_65": { "PT-BR": "Gema Poderosa", "EN": "Power Gem", - "FI": "Power Gem", - "NL": "Power Gem", - "NO": "Power Gem", - "PL": "Power Gem", "FR": "Rayon Gemme", "DE": "Juwelenkraft", "IT": "Gemmoforza", @@ -847,10 +582,6 @@ "pokemon_move_66": { "PT-BR": "Furtividade nas Sombras", "EN": "Shadow Sneak", - "FI": "Shadow Sneak", - "NL": "Shadow Sneak", - "NO": "Shadow Sneak", - "PL": "Shadow Sneak", "FR": "Ombre Portée", "DE": "Schattenstoß", "IT": "Furtivombra", @@ -860,23 +591,15 @@ "pokemon_move_67": { "PT-BR": "Soco Sombrio", "EN": "Shadow Punch", - "FI": "Shadow Punch", - "NL": "Shadow Punch", - "NO": "Shadow Punch", - "PL": "Shadow Punch", "FR": "Poing Ombre", "DE": "Finsterfaust", "IT": "Pugnodombra", - "RU": "Теневой Кулак", + "RU": "Теневая форма Кулак", "ES": "Puño Sombra" }, "pokemon_move_68": { "PT-BR": "Garra Sombria", "EN": "Shadow Claw", - "FI": "Shadow Claw", - "NL": "Shadow Claw", - "NO": "Shadow Claw", - "PL": "Shadow Claw", "FR": "Griffe Ombre", "DE": "Dunkelklaue", "IT": "Ombrartigli", @@ -886,10 +609,6 @@ "pokemon_move_69": { "PT-BR": "Vento Ominoso", "EN": "Ominous Wind", - "FI": "Ominous Wind", - "NL": "Ominous Wind", - "NO": "Ominous Wind", - "PL": "Ominous Wind", "FR": "Vent Mauvais", "DE": "Unheilböen", "IT": "Funestovento", @@ -899,10 +618,6 @@ "pokemon_move_70": { "PT-BR": "Bola Sombria", "EN": "Shadow Ball", - "FI": "Shadow Ball", - "NL": "Shadow Ball", - "NO": "Shadow Ball", - "PL": "Shadow Ball", "FR": "Ball’Ombre", "DE": "Spukball", "IT": "Palla Ombra", @@ -912,10 +627,6 @@ "pokemon_move_71": { "PT-BR": "Soco Projétil", "EN": "Bullet Punch", - "FI": "Bullet Punch", - "NL": "Bullet Punch", - "NO": "Bullet Punch", - "PL": "Bullet Punch", "FR": "Pisto-Poing", "DE": "Patronenhieb", "IT": "Pugnoscarica", @@ -925,10 +636,6 @@ "pokemon_move_72": { "PT-BR": "Bomba Ímã", "EN": "Magnet Bomb", - "FI": "Magnet Bomb", - "NL": "Magnet Bomb", - "NO": "Magnet Bomb", - "PL": "Magnet Bomb", "FR": "Bombe Aimant", "DE": "Magnetbombe", "IT": "Bombagnete", @@ -938,10 +645,6 @@ "pokemon_move_73": { "PT-BR": "Asa de Aço", "EN": "Steel Wing", - "FI": "Steel Wing", - "NL": "Steel Wing", - "NO": "Steel Wing", - "PL": "Steel Wing", "FR": "Ailes d’Acier", "DE": "Stahlflügel", "IT": "Alacciaio", @@ -951,10 +654,6 @@ "pokemon_move_74": { "PT-BR": "Cabeça de Ferro", "EN": "Iron Head", - "FI": "Iron Head", - "NL": "Iron Head", - "NO": "Iron Head", - "PL": "Iron Head", "FR": "Tête de Fer", "DE": "Eisenschädel", "IT": "Metaltestata", @@ -964,10 +663,6 @@ "pokemon_move_75": { "PT-BR": "Ataque Parabólico", "EN": "Parabolic Charge", - "FI": "Parabolic Charge", - "NL": "Parabolic Charge", - "NO": "Parabolic Charge", - "PL": "Parabolic Charge", "FR": "Parabocharge", "DE": "Parabolladung", "IT": "Caricaparabola", @@ -977,10 +672,6 @@ "pokemon_move_76": { "PT-BR": "Faísca", "EN": "Spark", - "FI": "Spark", - "NL": "Spark", - "NO": "Spark", - "PL": "Spark", "FR": "Étincelle", "DE": "Funkensprung", "IT": "Scintilla", @@ -990,10 +681,6 @@ "pokemon_move_77": { "PT-BR": "Soco Trovoada", "EN": "Thunder Punch", - "FI": "Thunder Punch", - "NL": "Thunder Punch", - "NO": "Thunder Punch", - "PL": "Thunder Punch", "FR": "Poing Éclair", "DE": "Donnerschlag", "IT": "Tuonopugno", @@ -1003,10 +690,6 @@ "pokemon_move_78": { "PT-BR": "Trovão", "EN": "Thunder", - "FI": "Thunder", - "NL": "Thunder", - "NO": "Thunder", - "PL": "Thunder", "FR": "Fatal-Foudre", "DE": "Donner", "IT": "Tuono", @@ -1016,10 +699,6 @@ "pokemon_move_79": { "PT-BR": "Relâmpago", "EN": "Thunderbolt", - "FI": "Thunderbolt", - "NL": "Thunderbolt", - "NO": "Thunderbolt", - "PL": "Thunderbolt", "FR": "Tonnerre", "DE": "Donnerblitz", "IT": "Fulmine", @@ -1027,12 +706,7 @@ "ES": "Rayo" }, "pokemon_move_80": { - "PT-BR": "Twister", "EN": "Twister", - "FI": "Twister", - "NL": "Twister", - "NO": "Twister", - "PL": "Twister", "FR": "Ouragan", "DE": "Windhose", "IT": "Tornado", @@ -1042,10 +716,6 @@ "pokemon_move_81": { "PT-BR": "Sopro do Dragão", "EN": "Dragon Breath", - "FI": "Dragon Breath", - "NL": "Dragon Breath", - "NO": "Dragon Breath", - "PL": "Dragon Breath", "FR": "Draco-Souffle", "DE": "Feuerodem", "IT": "Dragospiro", @@ -1055,10 +725,6 @@ "pokemon_move_82": { "PT-BR": "Pulso do Dragão", "EN": "Dragon Pulse", - "FI": "Dragon Pulse", - "NL": "Dragon Pulse", - "NO": "Dragon Pulse", - "PL": "Dragon Pulse", "FR": "Draco-Choc", "DE": "Drachenpuls", "IT": "Dragopulsar", @@ -1068,10 +734,6 @@ "pokemon_move_83": { "PT-BR": "Garra de Dragão", "EN": "Dragon Claw", - "FI": "Dragon Claw", - "NL": "Dragon Claw", - "NO": "Dragon Claw", - "PL": "Dragon Claw", "FR": "Draco-Griffe", "DE": "Drachenklaue", "IT": "Dragartigli", @@ -1081,10 +743,6 @@ "pokemon_move_84": { "PT-BR": "Voz Desarmante", "EN": "Disarming Voice", - "FI": "Disarming Voice", - "NL": "Disarming Voice", - "NO": "Disarming Voice", - "PL": "Disarming Voice", "FR": "Voix Enjôleuse", "DE": "Säuselstimme", "IT": "Incantavoce", @@ -1094,10 +752,6 @@ "pokemon_move_85": { "PT-BR": "Beijo Drenante", "EN": "Draining Kiss", - "FI": "Draining Kiss", - "NL": "Draining Kiss", - "NO": "Draining Kiss", - "PL": "Draining Kiss", "FR": "Vampibaiser", "DE": "Diebeskuss", "IT": "Assorbibacio", @@ -1107,10 +761,6 @@ "pokemon_move_86": { "PT-BR": "Clarão Deslumbrante", "EN": "Dazzling Gleam", - "FI": "Dazzling Gleam", - "NL": "Dazzling Gleam", - "NO": "Dazzling Gleam", - "PL": "Dazzling Gleam", "FR": "Éclat Magique", "DE": "Zauberschein", "IT": "Magibrillio", @@ -1120,10 +770,6 @@ "pokemon_move_87": { "PT-BR": "Explosão Lunar", "EN": "Moonblast", - "FI": "Moonblast", - "NL": "Moonblast", - "NO": "Moonblast", - "PL": "Moonblast", "FR": "Pouvoir Lunaire", "DE": "Mondgewalt", "IT": "Forza Lunare", @@ -1133,10 +779,6 @@ "pokemon_move_88": { "PT-BR": "Jogo Duro", "EN": "Play Rough", - "FI": "Play Rough", - "NL": "Play Rough", - "NO": "Play Rough", - "PL": "Play Rough", "FR": "Câlinerie", "DE": "Knuddler", "IT": "Carineria", @@ -1146,10 +788,6 @@ "pokemon_move_89": { "PT-BR": "Corte-veneno", "EN": "Cross Poison", - "FI": "Cross Poison", - "NL": "Cross Poison", - "NO": "Cross Poison", - "PL": "Cross Poison", "FR": "Poison Croix", "DE": "Giftstreich", "IT": "Velenocroce", @@ -1159,10 +797,6 @@ "pokemon_move_90": { "PT-BR": "Bomba de Lodo", "EN": "Sludge Bomb", - "FI": "Sludge Bomb", - "NL": "Sludge Bomb", - "NO": "Sludge Bomb", - "PL": "Sludge Bomb", "FR": "Bombe Beurk", "DE": "Matschbombe", "IT": "Fangobomba", @@ -1172,10 +806,6 @@ "pokemon_move_91": { "PT-BR": "Onda de Lama", "EN": "Sludge Wave", - "FI": "Sludge Wave", - "NL": "Sludge Wave", - "NO": "Sludge Wave", - "PL": "Sludge Wave", "FR": "Cradovague", "DE": "Schlammwoge", "IT": "Fangonda", @@ -1185,10 +815,6 @@ "pokemon_move_92": { "PT-BR": "Tiro de Sujeira", "EN": "Gunk Shot", - "FI": "Gunk Shot", - "NL": "Gunk Shot", - "NO": "Gunk Shot", - "PL": "Gunk Shot", "FR": "Détricanon", "DE": "Mülltreffer", "IT": "Sporcolancio", @@ -1198,10 +824,6 @@ "pokemon_move_93": { "PT-BR": "Tiro de Lama", "EN": "Mud Shot", - "FI": "Mud Shot", - "NL": "Mud Shot", - "NO": "Mud Shot", - "PL": "Mud Shot", "FR": "Tir de Boue", "DE": "Lehmschuss", "IT": "Colpodifango", @@ -1211,10 +833,6 @@ "pokemon_move_94": { "PT-BR": "Bastão de Osso", "EN": "Bone Club", - "FI": "Bone Club", - "NL": "Bone Club", - "NO": "Bone Club", - "PL": "Bone Club", "FR": "Massd’Os", "DE": "Knochenkeule", "IT": "Ossoclava", @@ -1224,10 +842,6 @@ "pokemon_move_95": { "PT-BR": "Tremor", "EN": "Bulldoze", - "FI": "Bulldoze", - "NL": "Bulldoze", - "NO": "Bulldoze", - "PL": "Bulldoze", "FR": "Piétisol", "DE": "Dampfwalze", "IT": "Battiterra", @@ -1237,10 +851,6 @@ "pokemon_move_96": { "PT-BR": "Bomba de Lama", "EN": "Mud Bomb", - "FI": "Mud Bomb", - "NL": "Mud Bomb", - "NO": "Mud Bomb", - "PL": "Mud Bomb", "FR": "Boue-Bombe", "DE": "Schlammbombe", "IT": "Pantanobomba", @@ -1250,10 +860,6 @@ "pokemon_move_97": { "PT-BR": "Cortador de Fúria", "EN": "Fury Cutter", - "FI": "Fury Cutter", - "NL": "Fury Cutter", - "NO": "Fury Cutter", - "PL": "Fury Cutter", "FR": "Taillade", "DE": "Zornklinge", "IT": "Tagliofuria", @@ -1263,10 +869,6 @@ "pokemon_move_98": { "PT-BR": "Picada", "EN": "Bug Bite", - "FI": "Bug Bite", - "NL": "Bug Bite", - "NO": "Bug Bite", - "PL": "Bug Bite", "FR": "Piqûre", "DE": "Käferbiss", "IT": "Coleomorso", @@ -1276,10 +878,6 @@ "pokemon_move_99": { "PT-BR": "Feixe Sinalizador", "EN": "Signal Beam", - "FI": "Signal Beam", - "NL": "Signal Beam", - "NO": "Signal Beam", - "PL": "Signal Beam", "FR": "Rayon Signal", "DE": "Ampelleuchte", "IT": "Segnoraggio", @@ -1289,23 +887,15 @@ "pokemon_move_100": { "PT-BR": "Tesoura X", "EN": "X-Scissor", - "FI": "X-Scissor", - "NL": "X-Scissor", - "NO": "X-Scissor", - "PL": "X-Scissor", "FR": "Plaie Croix", "DE": "Kreuzschere", "IT": "Forbice X", - "RU": "Икс-Ножницы", + "RU": "Икс-ножницы", "ES": "Tijera X" }, "pokemon_move_101": { "PT-BR": "Ataque de Chamas", "EN": "Flame Charge", - "FI": "Flame Charge", - "NL": "Flame Charge", - "NO": "Flame Charge", - "PL": "Flame Charge", "FR": "Nitrocharge", "DE": "Nitroladung", "IT": "Nitrocarica", @@ -1315,10 +905,6 @@ "pokemon_move_102": { "PT-BR": "Rajada de Chamas", "EN": "Flame Burst", - "FI": "Flame Burst", - "NL": "Flame Burst", - "NO": "Flame Burst", - "PL": "Flame Burst", "FR": "Rebondifeu", "DE": "Funkenflug", "IT": "Pirolancio", @@ -1328,10 +914,6 @@ "pokemon_move_103": { "PT-BR": "Rajada de Fogo", "EN": "Fire Blast", - "FI": "Fire Blast", - "NL": "Fire Blast", - "NO": "Fire Blast", - "PL": "Fire Blast", "FR": "Déflagration", "DE": "Feuersturm", "IT": "Fuocobomba", @@ -1341,10 +923,6 @@ "pokemon_move_104": { "PT-BR": "Salmoura", "EN": "Brine", - "FI": "Brine", - "NL": "Brine", - "NO": "Brine", - "PL": "Brine", "FR": "Saumure", "DE": "Lake", "IT": "Acquadisale", @@ -1354,10 +932,6 @@ "pokemon_move_105": { "PT-BR": "Pulso d'Água", "EN": "Water Pulse", - "FI": "Water Pulse", - "NL": "Water Pulse", - "NO": "Water Pulse", - "PL": "Water Pulse", "FR": "Vibraqua", "DE": "Aquawelle", "IT": "Idropulsar", @@ -1367,10 +941,6 @@ "pokemon_move_106": { "PT-BR": "Escaldada", "EN": "Scald", - "FI": "Scald", - "NL": "Scald", - "NO": "Scald", - "PL": "Scald", "FR": "Ébullition", "DE": "Siedewasser", "IT": "Idrovampata", @@ -1380,10 +950,6 @@ "pokemon_move_107": { "PT-BR": "Jato d'Água", "EN": "Hydro Pump", - "FI": "Hydro Pump", - "NL": "Hydro Pump", - "NO": "Hydro Pump", - "PL": "Hydro Pump", "FR": "Hydrocanon", "DE": "Hydropumpe", "IT": "Idropompa", @@ -1393,10 +959,6 @@ "pokemon_move_108": { "PT-BR": "Psíquico", "EN": "Psychic", - "FI": "Psychic", - "NL": "Psychic", - "NO": "Psychic", - "PL": "Psychic", "FR": "Psyko", "DE": "Psychokinese", "IT": "Psichico", @@ -1406,10 +968,6 @@ "pokemon_move_109": { "PT-BR": "Golpe Psíquico", "EN": "Psystrike", - "FI": "Psystrike", - "NL": "Psystrike", - "NO": "Psystrike", - "PL": "Psystrike", "FR": "Frappe Psy", "DE": "Psychostoß", "IT": "Psicobotta", @@ -1419,10 +977,6 @@ "pokemon_move_110": { "PT-BR": "Caco de Gelo", "EN": "Ice Shard", - "FI": "Ice Shard", - "NL": "Ice Shard", - "NO": "Ice Shard", - "PL": "Ice Shard", "FR": "Éclats Glace", "DE": "Eissplitter", "IT": "Geloscheggia", @@ -1432,10 +986,6 @@ "pokemon_move_111": { "PT-BR": "Vento Congelante", "EN": "Icy Wind", - "FI": "Icy Wind", - "NL": "Icy Wind", - "NO": "Icy Wind", - "PL": "Icy Wind", "FR": "Vent Glace", "DE": "Eissturm", "IT": "Ventogelato", @@ -1445,10 +995,6 @@ "pokemon_move_112": { "PT-BR": "Respiração de Gelo", "EN": "Frost Breath", - "FI": "Frost Breath", - "NL": "Frost Breath", - "NO": "Frost Breath", - "PL": "Frost Breath", "FR": "Souffle Glacé", "DE": "Eisesodem", "IT": "Alitogelido", @@ -1458,10 +1004,6 @@ "pokemon_move_113": { "PT-BR": "Absorção", "EN": "Absorb", - "FI": "Absorb", - "NL": "Absorb", - "NO": "Absorb", - "PL": "Absorb", "FR": "Vole-Vie", "DE": "Absorber", "IT": "Assorbimento", @@ -1471,23 +1013,15 @@ "pokemon_move_114": { "PT-BR": "Gigadreno", "EN": "Giga Drain", - "FI": "Giga Drain", - "NL": "Giga Drain", - "NO": "Giga Drain", - "PL": "Giga Drain", "FR": "Giga-Sangsue", "DE": "Gigasauger", "IT": "Gigassorbimento", - "RU": "Гига-Осушение", + "RU": "Гига-осушение", "ES": "Gigadrenado" }, "pokemon_move_115": { "PT-BR": "Soco de Fogo", "EN": "Fire Punch", - "FI": "Fire Punch", - "NL": "Fire Punch", - "NO": "Fire Punch", - "PL": "Fire Punch", "FR": "Poing Feu", "DE": "Feuerschlag", "IT": "Fuocopugno", @@ -1497,10 +1031,6 @@ "pokemon_move_116": { "PT-BR": "Raio Solar", "EN": "Solar Beam", - "FI": "Solar Beam", - "NL": "Solar Beam", - "NO": "Solar Beam", - "PL": "Solar Beam", "FR": "Lance-Soleil", "DE": "Solarstrahl", "IT": "Solarraggio", @@ -1510,23 +1040,15 @@ "pokemon_move_117": { "PT-BR": "Lâmina de Folha", "EN": "Leaf Blade", - "FI": "Leaf Blade", - "NL": "Leaf Blade", - "NO": "Leaf Blade", - "PL": "Leaf Blade", "FR": "Lame Feuille", "DE": "Laubklinge", "IT": "Fendifoglia", - "RU": "Лист-Лезвие", + "RU": "Лист-лезвие", "ES": "Hoja Aguda" }, "pokemon_move_118": { "PT-BR": "Chicote Poderoso", "EN": "Power Whip", - "FI": "Power Whip", - "NL": "Power Whip", - "NO": "Power Whip", - "PL": "Power Whip", "FR": "Mégafouet", "DE": "Blattgeißel", "IT": "Vigorcolpo", @@ -1536,23 +1058,14 @@ "pokemon_move_119": { "PT-BR": "Borrifada", "EN": "Splash", - "FI": "Splash", - "NL": "Splash", - "NO": "Splash", - "PL": "Splash", "FR": "Trempette", "DE": "Platscher", - "IT": "Splash", "RU": "Всплеск", "ES": "Salpicadura" }, "pokemon_move_120": { "PT-BR": "Ácido", "EN": "Acid", - "FI": "Acid", - "NL": "Acid", - "NO": "Acid", - "PL": "Acid", "FR": "Acide", "DE": "Säure", "IT": "Acido", @@ -1562,10 +1075,6 @@ "pokemon_move_121": { "PT-BR": "Cortador de Ar", "EN": "Air Cutter", - "FI": "Air Cutter", - "NL": "Air Cutter", - "NO": "Air Cutter", - "PL": "Air Cutter", "FR": "Tranch’Air", "DE": "Windschnitt", "IT": "Aerasoio", @@ -1575,10 +1084,6 @@ "pokemon_move_122": { "PT-BR": "Furacão", "EN": "Hurricane", - "FI": "Hurricane", - "NL": "Hurricane", - "NO": "Hurricane", - "PL": "Hurricane", "FR": "Vent Violent", "DE": "Orkan", "IT": "Tifone", @@ -1588,10 +1093,6 @@ "pokemon_move_123": { "PT-BR": "Quebra-telha", "EN": "Brick Break", - "FI": "Brick Break", - "NL": "Brick Break", - "NO": "Brick Break", - "PL": "Brick Break", "FR": "Casse-Brique", "DE": "Durchbruch", "IT": "Breccia", @@ -1601,10 +1102,6 @@ "pokemon_move_124": { "PT-BR": "Cortar", "EN": "Cut", - "FI": "Cut", - "NL": "Cut", - "NO": "Cut", - "PL": "Cut", "FR": "Coupe", "DE": "Zerschneider", "IT": "Taglio", @@ -1614,10 +1111,6 @@ "pokemon_move_125": { "PT-BR": "Ataque Veloz", "EN": "Swift", - "FI": "Swift", - "NL": "Swift", - "NO": "Swift", - "PL": "Swift", "FR": "Météores", "DE": "Sternschauer", "IT": "Comete", @@ -1627,10 +1120,6 @@ "pokemon_move_126": { "PT-BR": "Ataque de Chifre", "EN": "Horn Attack", - "FI": "Horn Attack", - "NL": "Horn Attack", - "NO": "Horn Attack", - "PL": "Horn Attack", "FR": "Koud’Korne", "DE": "Hornattacke", "IT": "Incornata", @@ -1640,10 +1129,6 @@ "pokemon_move_127": { "PT-BR": "Pisotear", "EN": "Stomp", - "FI": "Stomp", - "NL": "Stomp", - "NO": "Stomp", - "PL": "Stomp", "FR": "Écrasement", "DE": "Stampfer", "IT": "Pestone", @@ -1653,10 +1138,6 @@ "pokemon_move_128": { "PT-BR": "Cabeçada", "EN": "Headbutt", - "FI": "Headbutt", - "NL": "Headbutt", - "NO": "Headbutt", - "PL": "Headbutt", "FR": "Coup d’Boule", "DE": "Kopfnuss", "IT": "Bottintesta", @@ -1666,10 +1147,6 @@ "pokemon_move_129": { "PT-BR": "Hiperpresa", "EN": "Hyper Fang", - "FI": "Hyper Fang", - "NL": "Hyper Fang", - "NO": "Hyper Fang", - "PL": "Hyper Fang", "FR": "Croc de Mort", "DE": "Hyperzahn", "IT": "Iperzanna", @@ -1679,12 +1156,7 @@ "pokemon_move_130": { "PT-BR": "Pancada Brusca", "EN": "Slam", - "FI": "Slam", - "NL": "Slam", - "NO": "Slam", - "PL": "Slam", "FR": "Souplesse", - "DE": "Slam", "IT": "Schianto", "RU": "Таранный Удар", "ES": "Atizar" @@ -1692,10 +1164,6 @@ "pokemon_move_131": { "PT-BR": "Pancada Corporal", "EN": "Body Slam", - "FI": "Body Slam", - "NL": "Body Slam", - "NO": "Body Slam", - "PL": "Body Slam", "FR": "Plaquage", "DE": "Bodyslam", "IT": "Corposcontro", @@ -1705,10 +1173,6 @@ "pokemon_move_132": { "PT-BR": "Descansar", "EN": "Rest", - "FI": "Rest", - "NL": "Rest", - "NO": "Rest", - "PL": "Rest", "FR": "Repos", "DE": "Erholung", "IT": "Riposo", @@ -1718,10 +1182,6 @@ "pokemon_move_133": { "PT-BR": "Insistência", "EN": "Struggle", - "FI": "Struggle", - "NL": "Struggle", - "NO": "Struggle", - "PL": "Struggle", "FR": "Lutte", "DE": "Verzweifler", "IT": "Scontro", @@ -1731,10 +1191,6 @@ "pokemon_move_134": { "PT-BR": "Escaldada", "EN": "Scald", - "FI": "Scald", - "NL": "Scald", - "NO": "Scald", - "PL": "Scald", "FR": "Ébullition", "DE": "Siedewasser", "IT": "Idrovampata", @@ -1744,10 +1200,6 @@ "pokemon_move_135": { "PT-BR": "Jato d'Água", "EN": "Hydro Pump", - "FI": "Hydro Pump", - "NL": "Hydro Pump", - "NO": "Hydro Pump", - "PL": "Hydro Pump", "FR": "Hydrocanon", "DE": "Hydropumpe", "IT": "Idropompa", @@ -1757,10 +1209,6 @@ "pokemon_move_136": { "PT-BR": "Embrulho", "EN": "Wrap", - "FI": "Wrap", - "NL": "Wrap", - "NO": "Wrap", - "PL": "Wrap", "FR": "Ligotage", "DE": "Wickel", "IT": "Avvolgibotta", @@ -1770,10 +1218,6 @@ "pokemon_move_137": { "PT-BR": "Embrulho", "EN": "Wrap", - "FI": "Wrap", - "NL": "Wrap", - "NO": "Wrap", - "PL": "Wrap", "FR": "Ligotage", "DE": "Wickel", "IT": "Avvolgibotta", @@ -1783,10 +1227,6 @@ "pokemon_move_200": { "PT-BR": "Cortador de Fúria", "EN": "Fury Cutter", - "FI": "Fury Cutter", - "NL": "Fury Cutter", - "NO": "Fury Cutter", - "PL": "Fury Cutter", "FR": "Taillade", "DE": "Zornklinge", "IT": "Tagliofuria", @@ -1796,10 +1236,6 @@ "pokemon_move_201": { "PT-BR": "Picada", "EN": "Bug Bite", - "FI": "Bug Bite", - "NL": "Bug Bite", - "NO": "Bug Bite", - "PL": "Bug Bite", "FR": "Piqûre", "DE": "Käferbiss", "IT": "Coleomorso", @@ -1809,10 +1245,6 @@ "pokemon_move_202": { "PT-BR": "Mordida", "EN": "Bite", - "FI": "Bite", - "NL": "Bite", - "NO": "Bite", - "PL": "Bite", "FR": "Morsure", "DE": "Biss", "IT": "Morso", @@ -1822,10 +1254,6 @@ "pokemon_move_203": { "PT-BR": "Soco Enganador", "EN": "Sucker Punch", - "FI": "Sucker Punch", - "NL": "Sucker Punch", - "NO": "Sucker Punch", - "PL": "Sucker Punch", "FR": "Coup Bas", "DE": "Tiefschlag", "IT": "Sbigoattacco", @@ -1835,10 +1263,6 @@ "pokemon_move_204": { "PT-BR": "Sopro do Dragão", "EN": "Dragon Breath", - "FI": "Dragon Breath", - "NL": "Dragon Breath", - "NO": "Dragon Breath", - "PL": "Dragon Breath", "FR": "Draco-Souffle", "DE": "Feuerodem", "IT": "Dragospiro", @@ -1848,10 +1272,6 @@ "pokemon_move_205": { "PT-BR": "Trovoada de Choques", "EN": "Thunder Shock", - "FI": "Thunder Shock", - "NL": "Thunder Shock", - "NO": "Thunder Shock", - "PL": "Thunder Shock", "FR": "Éclair", "DE": "Donnerschock", "IT": "Tuonoshock", @@ -1861,10 +1281,6 @@ "pokemon_move_206": { "PT-BR": "Faísca", "EN": "Spark", - "FI": "Spark", - "NL": "Spark", - "NO": "Spark", - "PL": "Spark", "FR": "Étincelle", "DE": "Funkensprung", "IT": "Scintilla", @@ -1874,10 +1290,6 @@ "pokemon_move_207": { "PT-BR": "Rasteira", "EN": "Low Kick", - "FI": "Low Kick", - "NL": "Low Kick", - "NO": "Low Kick", - "PL": "Low Kick", "FR": "Balayage", "DE": "Fußkick", "IT": "Colpo Basso", @@ -1887,10 +1299,6 @@ "pokemon_move_208": { "PT-BR": "Golpe de Caratê", "EN": "Karate Chop", - "FI": "Karate Chop", - "NL": "Karate Chop", - "NO": "Karate Chop", - "PL": "Karate Chop", "FR": "Poing Karaté", "DE": "Karateschlag", "IT": "Colpokarate", @@ -1900,10 +1308,6 @@ "pokemon_move_209": { "PT-BR": "Brasa", "EN": "Ember", - "FI": "Ember", - "NL": "Ember", - "NO": "Ember", - "PL": "Ember", "FR": "Flammèche", "DE": "Glut", "IT": "Braciere", @@ -1913,10 +1317,6 @@ "pokemon_move_210": { "PT-BR": "Ataque de Asa", "EN": "Wing Attack", - "FI": "Wing Attack", - "NL": "Wing Attack", - "NO": "Wing Attack", - "PL": "Wing Attack", "FR": "Cru-Ailes", "DE": "Flügelschlag", "IT": "Attacco d’Ala", @@ -1926,10 +1326,6 @@ "pokemon_move_211": { "PT-BR": "Bicada", "EN": "Peck", - "FI": "Peck", - "NL": "Peck", - "NO": "Peck", - "PL": "Peck", "FR": "Picpic", "DE": "Pikser", "IT": "Beccata", @@ -1939,10 +1335,6 @@ "pokemon_move_212": { "PT-BR": "Lambida", "EN": "Lick", - "FI": "Lick", - "NL": "Lick", - "NO": "Lick", - "PL": "Lick", "FR": "Léchouille", "DE": "Schlecker", "IT": "Leccata", @@ -1952,10 +1344,6 @@ "pokemon_move_213": { "PT-BR": "Garra Sombria", "EN": "Shadow Claw", - "FI": "Shadow Claw", - "NL": "Shadow Claw", - "NO": "Shadow Claw", - "PL": "Shadow Claw", "FR": "Griffe Ombre", "DE": "Dunkelklaue", "IT": "Ombrartigli", @@ -1965,23 +1353,15 @@ "pokemon_move_214": { "PT-BR": "Chicote de Vinha", "EN": "Vine Whip", - "FI": "Vine Whip", - "NL": "Vine Whip", - "NO": "Vine Whip", - "PL": "Vine Whip", "FR": "Fouet Lianes", "DE": "Rankenhieb", "IT": "Frustata", - "RU": "Плеть-Лиана", + "RU": "Плеть-лиана", "ES": "Látigo Cepa" }, "pokemon_move_215": { "PT-BR": "Folha Navalha", "EN": "Razor Leaf", - "FI": "Razor Leaf", - "NL": "Razor Leaf", - "NO": "Razor Leaf", - "PL": "Razor Leaf", "FR": "Tranch’Herbe", "DE": "Rasierblatt", "IT": "Foglielama", @@ -1991,10 +1371,6 @@ "pokemon_move_216": { "PT-BR": "Tiro de Lama", "EN": "Mud Shot", - "FI": "Mud Shot", - "NL": "Mud Shot", - "NO": "Mud Shot", - "PL": "Mud Shot", "FR": "Tir de Boue", "DE": "Lehmschuss", "IT": "Colpodifango", @@ -2004,10 +1380,6 @@ "pokemon_move_217": { "PT-BR": "Caco de Gelo", "EN": "Ice Shard", - "FI": "Ice Shard", - "NL": "Ice Shard", - "NO": "Ice Shard", - "PL": "Ice Shard", "FR": "Éclats Glace", "DE": "Eissplitter", "IT": "Geloscheggia", @@ -2017,10 +1389,6 @@ "pokemon_move_218": { "PT-BR": "Respiração de Gelo", "EN": "Frost Breath", - "FI": "Frost Breath", - "NL": "Frost Breath", - "NO": "Frost Breath", - "PL": "Frost Breath", "FR": "Souffle Glacé", "DE": "Eisesodem", "IT": "Alitogelido", @@ -2030,10 +1398,6 @@ "pokemon_move_219": { "PT-BR": "Ataque Rápido", "EN": "Quick Attack", - "FI": "Quick Attack", - "NL": "Quick Attack", - "NO": "Quick Attack", - "PL": "Quick Attack", "FR": "Vive-Attaque", "DE": "Ruckzuckhieb", "IT": "Attacco Rapido", @@ -2043,10 +1407,6 @@ "pokemon_move_220": { "PT-BR": "Arranhão", "EN": "Scratch", - "FI": "Scratch", - "NL": "Scratch", - "NO": "Scratch", - "PL": "Scratch", "FR": "Griffe", "DE": "Kratzer", "IT": "Graffio", @@ -2056,12 +1416,7 @@ "pokemon_move_221": { "PT-BR": "Investida", "EN": "Tackle", - "FI": "Tackle", - "NL": "Tackle", - "NO": "Tackle", - "PL": "Tackle", "FR": "Charge", - "DE": "Tackle", "IT": "Azione", "RU": "Бросок", "ES": "Placaje" @@ -2069,10 +1424,6 @@ "pokemon_move_222": { "PT-BR": "Pancada", "EN": "Pound", - "FI": "Pound", - "NL": "Pound", - "NO": "Pound", - "PL": "Pound", "FR": "Écras’Face", "DE": "Klaps", "IT": "Botta", @@ -2082,10 +1433,6 @@ "pokemon_move_223": { "PT-BR": "Cortar", "EN": "Cut", - "FI": "Cut", - "NL": "Cut", - "NO": "Cut", - "PL": "Cut", "FR": "Coupe", "DE": "Zerschneider", "IT": "Taglio", @@ -2095,10 +1442,6 @@ "pokemon_move_224": { "PT-BR": "Golpe Envenenado", "EN": "Poison Jab", - "FI": "Poison Jab", - "NL": "Poison Jab", - "NO": "Poison Jab", - "PL": "Poison Jab", "FR": "Direct Toxik", "DE": "Gifthieb", "IT": "Velenpuntura", @@ -2108,10 +1451,6 @@ "pokemon_move_225": { "PT-BR": "Ácido", "EN": "Acid", - "FI": "Acid", - "NL": "Acid", - "NO": "Acid", - "PL": "Acid", "FR": "Acide", "DE": "Säure", "IT": "Acido", @@ -2121,10 +1460,6 @@ "pokemon_move_226": { "PT-BR": "Corte Psíquico", "EN": "Psycho Cut", - "FI": "Psycho Cut", - "NL": "Psycho Cut", - "NO": "Psycho Cut", - "PL": "Psycho Cut", "FR": "Coupe Psycho", "DE": "Psychoklinge", "IT": "Psicotaglio", @@ -2134,10 +1469,6 @@ "pokemon_move_227": { "PT-BR": "Lançamento de Rocha", "EN": "Rock Throw", - "FI": "Rock Throw", - "NL": "Rock Throw", - "NO": "Rock Throw", - "PL": "Rock Throw", "FR": "Jet-Pierres", "DE": "Steinwurf", "IT": "Sassata", @@ -2147,10 +1478,6 @@ "pokemon_move_228": { "PT-BR": "Garra de Metal", "EN": "Metal Claw", - "FI": "Metal Claw", - "NL": "Metal Claw", - "NO": "Metal Claw", - "PL": "Metal Claw", "FR": "Griffe Acier", "DE": "Metallklaue", "IT": "Ferrartigli", @@ -2160,10 +1487,6 @@ "pokemon_move_229": { "PT-BR": "Soco Projétil", "EN": "Bullet Punch", - "FI": "Bullet Punch", - "NL": "Bullet Punch", - "NO": "Bullet Punch", - "PL": "Bullet Punch", "FR": "Pisto-Poing", "DE": "Patronenhieb", "IT": "Pugnoscarica", @@ -2173,10 +1496,6 @@ "pokemon_move_230": { "PT-BR": "Revólver d'Água", "EN": "Water Gun", - "FI": "Water Gun", - "NL": "Water Gun", - "NO": "Water Gun", - "PL": "Water Gun", "FR": "Pistolet à O", "DE": "Aquaknarre", "IT": "Pistolacqua", @@ -2186,23 +1505,14 @@ "pokemon_move_231": { "PT-BR": "Borrifada", "EN": "Splash", - "FI": "Splash", - "NL": "Splash", - "NO": "Splash", - "PL": "Splash", "FR": "Trempette", "DE": "Platscher", - "IT": "Splash", "RU": "Всплеск", "ES": "Salpicadura" }, "pokemon_move_232": { "PT-BR": "Revólver d'Água", "EN": "Water Gun", - "FI": "Water Gun", - "NL": "Water Gun", - "NO": "Water Gun", - "PL": "Water Gun", "FR": "Pistolet à O", "DE": "Aquaknarre", "IT": "Pistolacqua", @@ -2212,10 +1522,6 @@ "pokemon_move_233": { "PT-BR": "Tapa de Lama", "EN": "Mud-Slap", - "FI": "Mud-Slap", - "NL": "Mud-Slap", - "NO": "Mud-Slap", - "PL": "Mud-Slap", "FR": "Coud’Boue", "DE": "Lehmschelle", "IT": "Fangosberla", @@ -2225,10 +1531,6 @@ "pokemon_move_234": { "PT-BR": "Cabeçada Zen", "EN": "Zen Headbutt", - "FI": "Zen Headbutt", - "NL": "Zen Headbutt", - "NO": "Zen Headbutt", - "PL": "Zen Headbutt", "FR": "Psykoud’Boul", "DE": "Zen-Kopfstoß", "IT": "Cozzata Zen", @@ -2238,10 +1540,6 @@ "pokemon_move_235": { "PT-BR": "Confusão", "EN": "Confusion", - "FI": "Confusion", - "NL": "Confusion", - "NO": "Confusion", - "PL": "Confusion", "FR": "Choc Mental", "DE": "Konfusion", "IT": "Confusione", @@ -2251,10 +1549,6 @@ "pokemon_move_236": { "PT-BR": "Ferrão Venenoso", "EN": "Poison Sting", - "FI": "Poison Sting", - "NL": "Poison Sting", - "NO": "Poison Sting", - "PL": "Poison Sting", "FR": "Dard-Venin", "DE": "Giftstachel", "IT": "Velenospina", @@ -2264,10 +1558,6 @@ "pokemon_move_237": { "PT-BR": "Bolha", "EN": "Bubble", - "FI": "Bubble", - "NL": "Bubble", - "NO": "Bubble", - "PL": "Bubble", "FR": "Écume", "DE": "Blubber", "IT": "Bolla", @@ -2277,10 +1567,6 @@ "pokemon_move_238": { "PT-BR": "Ataque Dissimulado", "EN": "Feint Attack", - "FI": "Feint Attack", - "NL": "Feint Attack", - "NO": "Feint Attack", - "PL": "Feint Attack", "FR": "Feinte", "DE": "Finte", "IT": "Finta", @@ -2290,10 +1576,6 @@ "pokemon_move_239": { "PT-BR": "Asa de Aço", "EN": "Steel Wing", - "FI": "Steel Wing", - "NL": "Steel Wing", - "NO": "Steel Wing", - "PL": "Steel Wing", "FR": "Ailes d’Acier", "DE": "Stahlflügel", "IT": "Alacciaio", @@ -2303,10 +1585,6 @@ "pokemon_move_240": { "PT-BR": "Presas de Fogo", "EN": "Fire Fang", - "FI": "Fire Fang", - "NL": "Fire Fang", - "NO": "Fire Fang", - "PL": "Fire Fang", "FR": "Crocs Feu", "DE": "Feuerzahn", "IT": "Rogodenti", @@ -2316,10 +1594,6 @@ "pokemon_move_241": { "PT-BR": "Esmagamento de Pedras", "EN": "Rock Smash", - "FI": "Rock Smash", - "NL": "Rock Smash", - "NO": "Rock Smash", - "PL": "Rock Smash", "FR": "Éclate-Roc", "DE": "Zertrümmerer", "IT": "Spaccaroccia", @@ -2329,10 +1603,6 @@ "pokemon_move_242": { "PT-BR": "Transformação", "EN": "Transform", - "FI": "Transform", - "NL": "Transform", - "NO": "Transform", - "PL": "Transform", "FR": "Morphing", "DE": "Wandler", "IT": "Trasformazione", @@ -2342,10 +1612,6 @@ "pokemon_move_243": { "PT-BR": "Contra-atacar", "EN": "Counter", - "FI": "Counter", - "NL": "Counter", - "NO": "Counter", - "PL": "Counter", "FR": "Riposte", "DE": "Konter", "IT": "Contrattacco", @@ -2355,10 +1621,6 @@ "pokemon_move_244": { "PT-BR": "Neve em Pó", "EN": "Powder Snow", - "FI": "Powder Snow", - "NL": "Powder Snow", - "NO": "Powder Snow", - "PL": "Powder Snow", "FR": "Poudreuse", "DE": "Pulverschnee", "IT": "Polneve", @@ -2368,11 +1630,6 @@ "pokemon_move_245": { "PT-BR": "Corpo-a-corpo", "EN": "Close Combat", - "FI": "Close Combat", - "NL": "Close Combat", - "NO": "Close Combat", - "PL": "Close Combat", - "FR": "Close Combat", "DE": "Nahkampf", "IT": "Zuffa", "RU": "Ближний Бой", @@ -2381,10 +1638,6 @@ "pokemon_move_246": { "PT-BR": "Soco Dinâmico", "EN": "Dynamic Punch", - "FI": "Dynamic Punch", - "NL": "Dynamic Punch", - "NO": "Dynamic Punch", - "PL": "Dynamic Punch", "FR": "Dynamo-Poing", "DE": "Wuchtschlag", "IT": "Dinamipugno", @@ -2394,10 +1647,6 @@ "pokemon_move_247": { "PT-BR": "Explosão Focalizada", "EN": "Focus Blast", - "FI": "Focus Blast", - "NL": "Focus Blast", - "NO": "Focus Blast", - "PL": "Focus Blast", "FR": "Exploforce", "DE": "Fokusstoß", "IT": "Focalcolpo", @@ -2407,10 +1656,6 @@ "pokemon_move_248": { "PT-BR": "Raio Aurora", "EN": "Aurora Beam", - "FI": "Aurora Beam", - "NL": "Aurora Beam", - "NO": "Aurora Beam", - "PL": "Aurora Beam", "FR": "Onde Boréale", "DE": "Aurorastrahl", "IT": "Raggiaurora", @@ -2420,10 +1665,6 @@ "pokemon_move_249": { "PT-BR": "Carga de Raio", "EN": "Charge Beam", - "FI": "Charge Beam", - "NL": "Charge Beam", - "NO": "Charge Beam", - "PL": "Charge Beam", "FR": "Rayon Chargé", "DE": "Ladestrahl", "IT": "Raggioscossa", @@ -2433,10 +1674,6 @@ "pokemon_move_250": { "PT-BR": "Troca Elétrica", "EN": "Volt Switch", - "FI": "Volt Switch", - "NL": "Volt Switch", - "NO": "Volt Switch", - "PL": "Volt Switch", "FR": "Change Éclair", "DE": "Voltwechsel", "IT": "Invertivolt", @@ -2446,10 +1683,6 @@ "pokemon_move_251": { "PT-BR": "Ataque Selvagem", "EN": "Wild Charge", - "FI": "Wild Charge", - "NL": "Wild Charge", - "NO": "Wild Charge", - "PL": "Wild Charge", "FR": "Éclair Fou", "DE": "Stromstoß", "IT": "Sprizzalampo", @@ -2459,10 +1692,6 @@ "pokemon_move_252": { "PT-BR": "Canhão Zap", "EN": "Zap Cannon", - "FI": "Zap Cannon", - "NL": "Zap Cannon", - "NO": "Zap Cannon", - "PL": "Zap Cannon", "FR": "Élecanon", "DE": "Blitzkanone", "IT": "Falcecannone", @@ -2472,10 +1701,6 @@ "pokemon_move_253": { "PT-BR": "Cauda do Dragão", "EN": "Dragon Tail", - "FI": "Dragon Tail", - "NL": "Dragon Tail", - "NO": "Dragon Tail", - "PL": "Dragon Tail", "FR": "Draco-Queue", "DE": "Drachenrute", "IT": "Codadrago", @@ -2483,13 +1708,7 @@ "ES": "Cola Dragón" }, "pokemon_move_254": { - "PT-BR": "Avalanche", "EN": "Avalanche", - "FI": "Avalanche", - "NL": "Avalanche", - "NO": "Avalanche", - "PL": "Avalanche", - "FR": "Avalanche", "DE": "Lawine", "IT": "Slavina", "RU": "Лавина", @@ -2498,10 +1717,6 @@ "pokemon_move_255": { "PT-BR": "Golpe de Ar", "EN": "Air Slash", - "FI": "Air Slash", - "NL": "Air Slash", - "NO": "Air Slash", - "PL": "Air Slash", "FR": "Lame d’Air", "DE": "Luftschnitt", "IT": "Eterelama", @@ -2511,10 +1726,6 @@ "pokemon_move_256": { "PT-BR": "Pássaro Bravo", "EN": "Brave Bird", - "FI": "Brave Bird", - "NL": "Brave Bird", - "NO": "Brave Bird", - "PL": "Brave Bird", "FR": "Rapace", "DE": "Sturzflug", "IT": "Baldeali", @@ -2524,10 +1735,6 @@ "pokemon_move_257": { "PT-BR": "Ataque do Céu", "EN": "Sky Attack", - "FI": "Sky Attack", - "NL": "Sky Attack", - "NO": "Sky Attack", - "PL": "Sky Attack", "FR": "Piqué", "DE": "Himmelsfeger", "IT": "Aeroattacco", @@ -2537,10 +1744,6 @@ "pokemon_move_258": { "PT-BR": "Fosso de Areia", "EN": "Sand Tomb", - "FI": "Sand Tomb", - "NL": "Sand Tomb", - "NO": "Sand Tomb", - "PL": "Sand Tomb", "FR": "Tourbi-Sable", "DE": "Sandgrab", "IT": "Sabbiotomba", @@ -2550,10 +1753,6 @@ "pokemon_move_259": { "PT-BR": "Explosão de Rocha", "EN": "Rock Blast", - "FI": "Rock Blast", - "NL": "Rock Blast", - "NO": "Rock Blast", - "PL": "Rock Blast", "FR": "Boule Roc", "DE": "Felswurf", "IT": "Cadutamassi", @@ -2563,10 +1762,6 @@ "pokemon_move_260": { "PT-BR": "Infestação", "EN": "Infestation", - "FI": "Infestation", - "NL": "Infestation", - "NO": "Infestation", - "PL": "Infestation", "FR": "Harcèlement", "DE": "Plage", "IT": "Assillo", @@ -2576,10 +1771,6 @@ "pokemon_move_261": { "PT-BR": "Ira de Inseto", "EN": "Struggle Bug", - "FI": "Struggle Bug", - "NL": "Struggle Bug", - "NO": "Struggle Bug", - "PL": "Struggle Bug", "FR": "Survinsecte", "DE": "Käfertrutz", "IT": "Entomoblocco", @@ -2589,10 +1780,6 @@ "pokemon_move_262": { "PT-BR": "Vento Prateado", "EN": "Silver Wind", - "FI": "Silver Wind", - "NL": "Silver Wind", - "NO": "Silver Wind", - "PL": "Silver Wind", "FR": "Vent Argenté", "DE": "Silberhauch", "IT": "Ventargenteo", @@ -2602,10 +1789,6 @@ "pokemon_move_263": { "PT-BR": "Abismar", "EN": "Astonish", - "FI": "Astonish", - "NL": "Astonish", - "NO": "Astonish", - "PL": "Astonish", "FR": "Étonnement", "DE": "Erstauner", "IT": "Sgomento", @@ -2615,10 +1798,6 @@ "pokemon_move_264": { "PT-BR": "Feitiço", "EN": "Hex", - "FI": "Hex", - "NL": "Hex", - "NO": "Hex", - "PL": "Hex", "FR": "Châtiment", "DE": "Bürde", "IT": "Sciagura", @@ -2628,10 +1807,6 @@ "pokemon_move_265": { "PT-BR": "Sombra Noturna", "EN": "Night Shade", - "FI": "Night Shade", - "NL": "Night Shade", - "NO": "Night Shade", - "PL": "Night Shade", "FR": "Ombre Nocturne", "DE": "Nachtnebel", "IT": "Ombra Notturna", @@ -2641,10 +1816,6 @@ "pokemon_move_266": { "PT-BR": "Cauda de Ferro", "EN": "Iron Tail", - "FI": "Iron Tail", - "NL": "Iron Tail", - "NO": "Iron Tail", - "PL": "Iron Tail", "FR": "Queue de Fer", "DE": "Eisenschweif", "IT": "Codacciaio", @@ -2654,23 +1825,15 @@ "pokemon_move_267": { "PT-BR": "Girobola", "EN": "Gyro Ball", - "FI": "Gyro Ball", - "NL": "Gyro Ball", - "NO": "Gyro Ball", - "PL": "Gyro Ball", "FR": "Gyroballe", "DE": "Gyroball", "IT": "Vortexpalla", - "RU": "Гиро-Шар", + "RU": "Гиро-шар", "ES": "Giro Bola" }, "pokemon_move_268": { "PT-BR": "Golpe Pesado", "EN": "Heavy Slam", - "FI": "Heavy Slam", - "NL": "Heavy Slam", - "NO": "Heavy Slam", - "PL": "Heavy Slam", "FR": "Tacle Lourd", "DE": "Rammboss", "IT": "Pesobomba", @@ -2680,10 +1843,6 @@ "pokemon_move_269": { "PT-BR": "Chama Furacão", "EN": "Fire Spin", - "FI": "Fire Spin", - "NL": "Fire Spin", - "NO": "Fire Spin", - "PL": "Fire Spin", "FR": "Danse Flammes", "DE": "Feuerwirbel", "IT": "Turbofuoco", @@ -2693,10 +1852,6 @@ "pokemon_move_270": { "PT-BR": "Superaquecimento", "EN": "Overheat", - "FI": "Overheat", - "NL": "Overheat", - "NO": "Overheat", - "PL": "Overheat", "FR": "Surchauffe", "DE": "Hitzekoller", "IT": "Vampata", @@ -2706,10 +1861,6 @@ "pokemon_move_271": { "PT-BR": "Projétil de Semente", "EN": "Bullet Seed", - "FI": "Bullet Seed", - "NL": "Bullet Seed", - "NO": "Bullet Seed", - "PL": "Bullet Seed", "FR": "Balle Graine", "DE": "Kugelsaat", "IT": "Semitraglia", @@ -2719,10 +1870,6 @@ "pokemon_move_272": { "PT-BR": "Nó de Grama", "EN": "Grass Knot", - "FI": "Grass Knot", - "NL": "Grass Knot", - "NO": "Grass Knot", - "PL": "Grass Knot", "FR": "Nœud Herbe", "DE": "Strauchler", "IT": "Laccioerboso", @@ -2732,10 +1879,6 @@ "pokemon_move_273": { "PT-BR": "Bola de Energia", "EN": "Energy Ball", - "FI": "Energy Ball", - "NL": "Energy Ball", - "NO": "Energy Ball", - "PL": "Energy Ball", "FR": "Éco-Sphère", "DE": "Energieball", "IT": "Energipalla", @@ -2745,10 +1888,6 @@ "pokemon_move_274": { "PT-BR": "Extrassensorial", "EN": "Extrasensory", - "FI": "Extrasensory", - "NL": "Extrasensory", - "NO": "Extrasensory", - "PL": "Extrasensory", "FR": "Extrasenseur", "DE": "Sondersensor", "IT": "Extrasenso", @@ -2758,10 +1897,6 @@ "pokemon_move_275": { "PT-BR": "Visão do Futuro", "EN": "Future Sight", - "FI": "Future Sight", - "NL": "Future Sight", - "NO": "Future Sight", - "PL": "Future Sight", "FR": "Prescience", "DE": "Seher", "IT": "Divinazione", @@ -2771,10 +1906,6 @@ "pokemon_move_276": { "PT-BR": "Casaco Espelhado", "EN": "Mirror Coat", - "FI": "Mirror Coat", - "NL": "Mirror Coat", - "NO": "Mirror Coat", - "PL": "Mirror Coat", "FR": "Voile Miroir", "DE": "Spiegelcape", "IT": "Specchiovelo", @@ -2784,10 +1915,6 @@ "pokemon_move_277": { "PT-BR": "Ultraje", "EN": "Outrage", - "FI": "Outrage", - "NL": "Outrage", - "NO": "Outrage", - "PL": "Outrage", "FR": "Colère", "DE": "Wutanfall", "IT": "Oltraggio", @@ -2797,10 +1924,6 @@ "pokemon_move_278": { "PT-BR": "Rosnado", "EN": "Snarl", - "FI": "Snarl", - "NL": "Snarl", - "NO": "Snarl", - "PL": "Snarl", "FR": "Aboiement", "DE": "Standpauke", "IT": "Urlorabbia", @@ -2810,10 +1933,6 @@ "pokemon_move_279": { "PT-BR": "Mastigada", "EN": "Crunch", - "FI": "Crunch", - "NL": "Crunch", - "NO": "Crunch", - "PL": "Crunch", "FR": "Mâchouille", "DE": "Knirscher", "IT": "Sgranocchio", @@ -2823,10 +1942,6 @@ "pokemon_move_280": { "PT-BR": "Jogo Sujo", "EN": "Foul Play", - "FI": "Foul Play", - "NL": "Foul Play", - "NO": "Foul Play", - "PL": "Foul Play", "FR": "Tricherie", "DE": "Schmarotzer", "IT": "Ripicca", @@ -2836,10 +1951,6 @@ "pokemon_move_281": { "PT-BR": "Poder Oculto", "EN": "Hidden Power", - "FI": "Hidden Power", - "NL": "Hidden Power", - "NO": "Hidden Power", - "PL": "Hidden Power", "FR": "Puissance Cachée", "DE": "Kraftreserve", "IT": "Introforza", @@ -2849,10 +1960,6 @@ "pokemon_move_282": { "PT-BR": "Desmantelar", "EN": "Take Down", - "FI": "Take Down", - "NL": "Take Down", - "NO": "Take Down", - "PL": "Take Down", "FR": "Bélier", "DE": "Bodycheck", "IT": "Riduttore", @@ -2862,10 +1969,6 @@ "pokemon_move_283": { "PT-BR": "Cachoeira", "EN": "Waterfall", - "FI": "Waterfall", - "NL": "Waterfall", - "NO": "Waterfall", - "PL": "Waterfall", "FR": "Cascade", "DE": "Kaskade", "IT": "Cascata", @@ -2875,25 +1978,13 @@ "pokemon_move_284": { "PT-BR": "Surfar", "EN": "Surf", - "FI": "Surf", - "NL": "Surf", - "NO": "Surf", - "PL": "Surf", - "FR": "Surf", "DE": "Surfer", - "IT": "Surf", - "RU": "Сёрф", - "ES": "Surf" + "RU": "Сёрф" }, "pokemon_move_285": { "PT-BR": "Meteoro do Dragão", "EN": "Draco Meteor", - "FI": "Draco Meteor", - "NL": "Draco Meteor", - "NO": "Draco Meteor", - "PL": "Draco Meteor", "FR": "Draco-Météore", - "DE": "Draco Meteor", "IT": "Dragobolide", "RU": "Метеор Дракона", "ES": "Cometa Draco" @@ -2901,10 +1992,6 @@ "pokemon_move_286": { "PT-BR": "Desejo Cruel", "EN": "Doom Desire", - "FI": "Doom Desire", - "NL": "Doom Desire", - "NO": "Doom Desire", - "PL": "Doom Desire", "FR": "Carnareket", "DE": "Kismetwunsch", "IT": "Obbliderio", @@ -2914,10 +2001,6 @@ "pokemon_move_287": { "PT-BR": "Bocejo", "EN": "Yawn", - "FI": "Yawn", - "NL": "Yawn", - "NO": "Yawn", - "PL": "Yawn", "FR": "Bâillement", "DE": "Gähner", "IT": "Sbadiglio", @@ -2927,10 +2010,6 @@ "pokemon_move_288": { "PT-BR": "Impulso Psíquico", "EN": "Psycho Boost", - "FI": "Psycho Boost", - "NL": "Psycho Boost", - "NO": "Psycho Boost", - "PL": "Psycho Boost", "FR": "Psycho-Boost", "DE": "Psyschub", "IT": "Psicoslancio", @@ -2940,10 +2019,6 @@ "pokemon_move_289": { "PT-BR": "Pulso Original", "EN": "Origin Pulse", - "FI": "Origin Pulse", - "NL": "Origin Pulse", - "NO": "Origin Pulse", - "PL": "Origin Pulse", "FR": "Onde Originelle", "DE": "Ursprungswoge", "IT": "Primopulsar", @@ -2953,10 +2028,6 @@ "pokemon_move_290": { "PT-BR": "Lâmina Abissal", "EN": "Precipice Blades", - "FI": "Precipice Blades", - "NL": "Precipice Blades", - "NO": "Precipice Blades", - "PL": "Precipice Blades", "FR": "Lame Pangéenne", "DE": "Abgrundsklinge", "IT": "Spade Telluriche", @@ -2966,10 +2037,6 @@ "pokemon_move_291": { "PT-BR": "Presente", "EN": "Present", - "FI": "Present", - "NL": "Present", - "NO": "Present", - "PL": "Present", "FR": "Cadeau", "DE": "Geschenk", "IT": "Regalino", @@ -2979,10 +2046,6 @@ "pokemon_move_292": { "PT-BR": "Esfera Climática", "EN": "Weather Ball", - "FI": "Weather Ball", - "NL": "Weather Ball", - "NO": "Weather Ball", - "PL": "Weather Ball", "FR": "Ball’Météo", "DE": "Meteorologe", "IT": "Palla Clima", @@ -2992,10 +2055,6 @@ "pokemon_move_293": { "PT-BR": "Esfera Climática", "EN": "Weather Ball", - "FI": "Weather Ball", - "NL": "Weather Ball", - "NO": "Weather Ball", - "PL": "Weather Ball", "FR": "Ball’Météo", "DE": "Meteorologe", "IT": "Palla Clima", @@ -3005,10 +2064,6 @@ "pokemon_move_294": { "PT-BR": "Esfera Climática", "EN": "Weather Ball", - "FI": "Weather Ball", - "NL": "Weather Ball", - "NO": "Weather Ball", - "PL": "Weather Ball", "FR": "Ball’Météo", "DE": "Meteorologe", "IT": "Palla Clima", @@ -3018,10 +2073,6 @@ "pokemon_move_295": { "PT-BR": "Esfera Climática", "EN": "Weather Ball", - "FI": "Weather Ball", - "NL": "Weather Ball", - "NO": "Weather Ball", - "PL": "Weather Ball", "FR": "Ball’Météo", "DE": "Meteorologe", "IT": "Palla Clima", @@ -3031,10 +2082,6 @@ "pokemon_move_296": { "PT-BR": "Planta Mortal", "EN": "Frenzy Plant", - "FI": "Frenzy Plant", - "NL": "Frenzy Plant", - "NO": "Frenzy Plant", - "PL": "Frenzy Plant", "FR": "Végé-Attaque", "DE": "Flora-Statue", "IT": "Radicalbero", @@ -3044,10 +2091,6 @@ "pokemon_move_297": { "PT-BR": "Derrubada", "EN": "Smack Down", - "FI": "Smack Down", - "NL": "Smack Down", - "NO": "Smack Down", - "PL": "Smack Down", "FR": "Anti-Air", "DE": "Katapult", "IT": "Abbattimento", @@ -3057,10 +2100,6 @@ "pokemon_move_298": { "PT-BR": "Queimadura Explosiva", "EN": "Blast Burn", - "FI": "Blast Burn", - "NL": "Blast Burn", - "NO": "Blast Burn", - "PL": "Blast Burn", "FR": "Rafale Feu", "DE": "Lohekanonade", "IT": "Incendio", @@ -3070,10 +2109,6 @@ "pokemon_move_299": { "PT-BR": "Hidro Canhão", "EN": "Hydro Cannon", - "FI": "Hydro Cannon", - "NL": "Hydro Cannon", - "NO": "Hydro Cannon", - "PL": "Hydro Cannon", "FR": "Hydroblast", "DE": "Aquahaubitze", "IT": "Idrocannone", @@ -3083,10 +2118,6 @@ "pokemon_move_300": { "PT-BR": "Último Recurso", "EN": "Last Resort", - "FI": "Last Resort", - "NL": "Last Resort", - "NO": "Last Resort", - "PL": "Last Resort", "FR": "Dernier Recours", "DE": "Zuflucht", "IT": "Ultimascelta", @@ -3096,10 +2127,6 @@ "pokemon_move_301": { "PT-BR": "Meteoro Esmagador", "EN": "Meteor Mash", - "FI": "Meteor Mash", - "NL": "Meteor Mash", - "NO": "Meteor Mash", - "PL": "Meteor Mash", "FR": "Poing Météore", "DE": "Sternenhieb", "IT": "Meteorpugno", @@ -3109,10 +2136,6 @@ "pokemon_move_302": { "PT-BR": "Quebra-crânio", "EN": "Skull Bash", - "FI": "Skull Bash", - "NL": "Skull Bash", - "NO": "Skull Bash", - "PL": "Skull Bash", "FR": "Coud’Krâne", "DE": "Schädelwumme", "IT": "Capocciata", @@ -3122,10 +2145,6 @@ "pokemon_move_303": { "PT-BR": "Spray Ácido", "EN": "Acid Spray", - "FI": "Acid Spray", - "NL": "Acid Spray", - "NO": "Acid Spray", - "PL": "Acid Spray", "FR": "Bombe Acide", "DE": "Säurespeier", "IT": "Acidobomba", @@ -3135,10 +2154,6 @@ "pokemon_move_304": { "PT-BR": "Poder da Terra", "EN": "Earth Power", - "FI": "Earth Power", - "NL": "Earth Power", - "NO": "Earth Power", - "PL": "Earth Power", "FR": "Telluriforce", "DE": "Erdkräfte", "IT": "Geoforza", @@ -3148,10 +2163,6 @@ "pokemon_move_305": { "PT-BR": "Martelo Caranguejo", "EN": "Crabhammer", - "FI": "Crabhammer", - "NL": "Crabhammer", - "NO": "Crabhammer", - "PL": "Crabhammer", "FR": "Pince-Masse", "DE": "Krabbhammer", "IT": "Martellata", @@ -3161,10 +2172,6 @@ "pokemon_move_306": { "PT-BR": "Estocada", "EN": "Lunge", - "FI": "Lunge", - "NL": "Lunge", - "NO": "Lunge", - "PL": "Lunge", "FR": "Furie-Bond", "DE": "Anfallen", "IT": "Assalto", @@ -3174,10 +2181,6 @@ "pokemon_move_307": { "PT-BR": "Garra Esmagadora", "EN": "Crush Claw", - "FI": "Crush Claw", - "NL": "Crush Claw", - "NO": "Crush Claw", - "PL": "Crush Claw", "FR": "Éclate Griffe", "DE": "Zermalmklaue", "IT": "Tritartigli", @@ -3187,23 +2190,12 @@ "pokemon_move_308": { "PT-BR": "Polvo-canhão", "EN": "Octazooka", - "FI": "Octazooka", - "NL": "Octazooka", - "NO": "Octazooka", - "PL": "Octazooka", - "FR": "Octazooka", - "DE": "Octazooka", - "IT": "Octazooka", "RU": "Чернильная Пушка", "ES": "Pulpocañón" }, "pokemon_move_309": { "PT-BR": "Tiro no Espelho", "EN": "Mirror Shot", - "FI": "Mirror Shot", - "NL": "Mirror Shot", - "NO": "Mirror Shot", - "PL": "Mirror Shot", "FR": "Miroi-Tir", "DE": "Spiegelsalve", "IT": "Cristalcolpo", @@ -3213,10 +2205,6 @@ "pokemon_move_310": { "PT-BR": "Superpoder", "EN": "Superpower", - "FI": "Superpower", - "NL": "Superpower", - "NO": "Superpower", - "PL": "Superpower", "FR": "Surpuissance", "DE": "Kraftkoloss", "IT": "Troppoforte", @@ -3226,10 +2214,6 @@ "pokemon_move_311": { "PT-BR": "Ferrão Letal", "EN": "Fell Stinger", - "FI": "Fell Stinger", - "NL": "Fell Stinger", - "NO": "Fell Stinger", - "PL": "Fell Stinger", "FR": "Dard Mortel", "DE": "Stachelfinale", "IT": "Pungiglione", @@ -3239,10 +2223,6 @@ "pokemon_move_312": { "PT-BR": "Tornado de Folhas", "EN": "Leaf Tornado", - "FI": "Leaf Tornado", - "NL": "Leaf Tornado", - "NO": "Leaf Tornado", - "PL": "Leaf Tornado", "FR": "Phytomixeur", "DE": "Grasmixer", "IT": "Vorticerba", @@ -3252,10 +2232,6 @@ "pokemon_move_313": { "PT-BR": "Suga-vidas", "EN": "Leech Life", - "FI": "Leech Life", - "NL": "Leech Life", - "NO": "Leech Life", - "PL": "Leech Life", "FR": "Vampirisme", "DE": "Blutsauger", "IT": "Sanguisuga", @@ -3265,10 +2241,6 @@ "pokemon_move_314": { "PT-BR": "Soco Dreno", "EN": "Drain Punch", - "FI": "Drain Punch", - "NL": "Drain Punch", - "NO": "Drain Punch", - "PL": "Drain Punch", "FR": "Vampi-Poing", "DE": "Ableithieb", "IT": "Assorbipugno", @@ -3278,10 +2250,6 @@ "pokemon_move_315": { "PT-BR": "Osso Sombrio", "EN": "Shadow Bone", - "FI": "Shadow Bone", - "NL": "Shadow Bone", - "NO": "Shadow Bone", - "PL": "Shadow Bone", "FR": "Os Ombre", "DE": "Schattenknochen", "IT": "Ossotetro", @@ -3291,10 +2259,6 @@ "pokemon_move_316": { "PT-BR": "Água Barrenta", "EN": "Muddy Water", - "FI": "Muddy Water", - "NL": "Muddy Water", - "NO": "Muddy Water", - "PL": "Muddy Water", "FR": "Ocroupi", "DE": "Lehmbrühe", "IT": "Fanghiglia", @@ -3304,10 +2268,6 @@ "pokemon_move_317": { "PT-BR": "Chute Labareda", "EN": "Blaze Kick", - "FI": "Blaze Kick", - "NL": "Blaze Kick", - "NO": "Blaze Kick", - "PL": "Blaze Kick", "FR": "Pied Brûleur", "DE": "Feuerfeger", "IT": "Calciardente", @@ -3317,10 +2277,6 @@ "pokemon_move_318": { "PT-BR": "Concha Navalha", "EN": "Razor Shell", - "FI": "Razor Shell", - "NL": "Razor Shell", - "NO": "Razor Shell", - "PL": "Razor Shell", "FR": "Coqui-Lame", "DE": "Kalkklinge", "IT": "Conchilama", @@ -3330,10 +2286,6 @@ "pokemon_move_319": { "PT-BR": "Soco Empoderador", "EN": "Power-Up Punch", - "FI": "Power-Up Punch", - "NL": "Power-Up Punch", - "NO": "Power-Up Punch", - "PL": "Power-Up Punch", "FR": "Poing Boost", "DE": "Steigerungshieb", "IT": "Crescipugno", @@ -3343,10 +2295,6 @@ "pokemon_move_320": { "PT-BR": "Encantar", "EN": "Charm", - "FI": "Charm", - "NL": "Charm", - "NO": "Charm", - "PL": "Charm", "FR": "Charme", "DE": "Charme", "IT": "Fascino", @@ -3356,25 +2304,14 @@ "pokemon_move_321": { "PT-BR": "Gigaimpacto", "EN": "Giga Impact", - "FI": "Giga Impact", - "NL": "Giga Impact", - "NO": "Giga Impact", - "PL": "Giga Impact", - "FR": "Giga Impact", "DE": "Gigastoß", "IT": "Gigaimpatto", - "RU": "Гига-Удар", + "RU": "Гига-удар", "ES": "Gigaimpacto" }, "pokemon_move_322": { "PT-BR": "Frustração", "EN": "Frustration", - "FI": "Frustration", - "NL": "Frustration", - "NO": "Frustration", - "PL": "Frustration", - "FR": "Frustration", - "DE": "Frustration", "IT": "Frustrazione", "RU": "Фрустрация", "ES": "Frustración" @@ -3382,10 +2319,6 @@ "pokemon_move_323": { "PT-BR": "Retorno", "EN": "Return", - "FI": "Return", - "NL": "Return", - "NO": "Return", - "PL": "Return", "FR": "Retour", "DE": "Rückkehr", "IT": "Ritorno", @@ -3395,10 +2328,6 @@ "pokemon_move_324": { "PT-BR": "Barulho Sincronizado", "EN": "Synchronoise", - "FI": "Synchronoise", - "NL": "Synchronoise", - "NO": "Synchronoise", - "PL": "Synchronoise", "FR": "Synchropeine", "DE": "Synchrolärm", "IT": "Sincrumore", @@ -3408,10 +2337,6 @@ "pokemon_move_325": { "PT-BR": "Mirar", "EN": "Lock-On", - "FI": "Lock-On", - "NL": "Lock-On", - "NO": "Lock-On", - "PL": "Lock-On", "FR": "Verrouillage", "DE": "Zielschuss", "IT": "Localizza", @@ -3421,10 +2346,6 @@ "pokemon_move_326": { "PT-BR": "Presa Trovejante", "EN": "Thunder Fang", - "FI": "Thunder Fang", - "NL": "Thunder Fang", - "NO": "Thunder Fang", - "PL": "Thunder Fang", "FR": "Crocs Éclair", "DE": "Donnerzahn", "IT": "Fulmindenti", @@ -3434,10 +2355,6 @@ "pokemon_move_327": { "PT-BR": "Presa de Gelo", "EN": "Ice Fang", - "FI": "Ice Fang", - "NL": "Ice Fang", - "NO": "Ice Fang", - "PL": "Ice Fang", "FR": "Crocs Givre", "DE": "Eiszahn", "IT": "Gelodenti", @@ -3447,10 +2364,6 @@ "pokemon_move_328": { "PT-BR": "Chifre Broca", "EN": "Horn Drill", - "FI": "Horn Drill", - "NL": "Horn Drill", - "NO": "Horn Drill", - "PL": "Horn Drill", "FR": "Empal’Korne", "DE": "Hornbohrer", "IT": "Perforcorno", @@ -3460,10 +2373,6 @@ "pokemon_move_329": { "PT-BR": "Fissura", "EN": "Fissure", - "FI": "Fissure", - "NL": "Fissure", - "NO": "Fissure", - "PL": "Fissure", "FR": "Abîme", "DE": "Geofissur", "IT": "Abisso", @@ -3473,10 +2382,6 @@ "pokemon_move_330": { "PT-BR": "Espada Sagrada", "EN": "Sacred Sword", - "FI": "Sacred Sword", - "NL": "Sacred Sword", - "NO": "Sacred Sword", - "PL": "Sacred Sword", "FR": "Lame Sainte", "DE": "Sanctoklinge", "IT": "Spadasolenne", @@ -3486,12 +2391,6 @@ "pokemon_move_331": { "PT-BR": "Aperto Voador", "EN": "Flying Press", - "FI": "Flying Press", - "NL": "Flying Press", - "NO": "Flying Press", - "PL": "Flying Press", - "FR": "Flying Press", - "DE": "Flying Press", "IT": "Schiacciatuffo", "RU": "Летающий Пресс", "ES": "Plancha Voladora" @@ -3499,10 +2398,6 @@ "pokemon_move_332": { "PT-BR": "Aura Esférica", "EN": "Aura Sphere", - "FI": "Aura Sphere", - "NL": "Aura Sphere", - "NO": "Aura Sphere", - "PL": "Aura Sphere", "FR": "Aurasphère", "DE": "Aurasphäre", "IT": "Forzasfera", @@ -3512,10 +2407,6 @@ "pokemon_move_333": { "PT-BR": "Revide", "EN": "Payback", - "FI": "Payback", - "NL": "Payback", - "NO": "Payback", - "PL": "Payback", "FR": "Représailles", "DE": "Gegenstoß", "IT": "Rivincita", @@ -3525,10 +2416,6 @@ "pokemon_move_334": { "PT-BR": "Demolidor de Pedras", "EN": "Rock Wrecker", - "FI": "Rock Wrecker", - "NL": "Rock Wrecker", - "NO": "Rock Wrecker", - "PL": "Rock Wrecker", "FR": "Roc-Boulet", "DE": "Felswerfer", "IT": "Devastomasso", @@ -3538,10 +2425,6 @@ "pokemon_move_335": { "PT-BR": "Explosão Aérea", "EN": "Aeroblast", - "FI": "Aeroblast", - "NL": "Aeroblast", - "NO": "Aeroblast", - "PL": "Aeroblast", "FR": "Aéroblast", "DE": "Luftstoß", "IT": "Aerocolpo", @@ -3551,10 +2434,6 @@ "pokemon_move_336": { "PT-BR": "Rajada Tecnológica", "EN": "Techno Blast", - "FI": "Techno Blast", - "NL": "Techno Blast", - "NO": "Techno Blast", - "PL": "Techno Blast", "FR": "Techno-Buster", "DE": "Techblaster", "IT": "Tecnobotto", @@ -3564,10 +2443,6 @@ "pokemon_move_337": { "PT-BR": "Rajada Tecnológica", "EN": "Techno Blast", - "FI": "Techno Blast", - "NL": "Techno Blast", - "NO": "Techno Blast", - "PL": "Techno Blast", "FR": "Techno-Buster", "DE": "Techblaster", "IT": "Tecnobotto", @@ -3577,10 +2452,6 @@ "pokemon_move_338": { "PT-BR": "Rajada Tecnológica", "EN": "Techno Blast", - "FI": "Techno Blast", - "NL": "Techno Blast", - "NO": "Techno Blast", - "PL": "Techno Blast", "FR": "Techno-Buster", "DE": "Techblaster", "IT": "Tecnobotto", @@ -3590,10 +2461,6 @@ "pokemon_move_339": { "PT-BR": "Rajada Tecnológica", "EN": "Techno Blast", - "FI": "Techno Blast", - "NL": "Techno Blast", - "NO": "Techno Blast", - "PL": "Techno Blast", "FR": "Techno-Buster", "DE": "Techblaster", "IT": "Tecnobotto", @@ -3603,10 +2470,6 @@ "pokemon_move_340": { "PT-BR": "Rajada Tecnológica", "EN": "Techno Blast", - "FI": "Techno Blast", - "NL": "Techno Blast", - "NO": "Techno Blast", - "PL": "Techno Blast", "FR": "Techno-Buster", "DE": "Techblaster", "IT": "Tecnobotto", @@ -3616,10 +2479,6 @@ "pokemon_move_341": { "PT-BR": "Voar", "EN": "Fly", - "FI": "Fly", - "NL": "Fly", - "NO": "Fly", - "PL": "Fly", "FR": "Vol", "DE": "Fliegen", "IT": "Volo", @@ -3629,10 +2488,6 @@ "pokemon_move_342": { "PT-BR": "Criação V", "EN": "V-create", - "FI": "V-create", - "NL": "V-create", - "NO": "V-create", - "PL": "V-create", "FR": "Coup Victoire", "DE": "V-Generator", "IT": "Generatore V", @@ -3642,10 +2497,6 @@ "pokemon_move_343": { "PT-BR": "Tempestade de Folhas", "EN": "Leaf Storm", - "FI": "Leaf Storm", - "NL": "Leaf Storm", - "NO": "Leaf Storm", - "PL": "Leaf Storm", "FR": "Tempête Verte", "DE": "Blättersturm", "IT": "Verdebufera", @@ -3655,10 +2506,6 @@ "pokemon_move_344": { "PT-BR": "Triataque", "EN": "Tri Attack", - "FI": "Tri Attack", - "NL": "Tri Attack", - "NO": "Tri Attack", - "PL": "Tri Attack", "FR": "Triplattaque", "DE": "Triplette", "IT": "Tripletta", @@ -3668,10 +2515,6 @@ "pokemon_move_345": { "PT-BR": "Lufada de Vento", "EN": "Gust", - "FI": "Gust", - "NL": "Gust", - "NO": "Gust", - "PL": "Gust", "FR": "Tornade", "DE": "Windstoß", "IT": "Raffica", @@ -3681,36 +2524,45 @@ "pokemon_move_346": { "PT-BR": "Incinerar", "EN": "Incinerate", - "FI": "Incinerate", - "NL": "Incinerate", - "NO": "Incinerate", - "PL": "Incinerate", "FR": "Calcination", "DE": "Einäschern", "IT": "Bruciatutto", - "RU": " Превращение в Пепел", + "RU": "Превращение в Пепел", "ES": "Calcinación" }, + "pokemon_move_347": { + "EN": "Dark Void", + "RU": "Темная Бездна" + }, "pokemon_move_348": { "PT-BR": "Dança das Penas", "EN": "Feather Dance", - "FI": "Feather Dance", - "NL": "Feather Dance", - "NO": "Feather Dance", - "PL": "Feather Dance", "FR": "Danse Plumes", "DE": "Daunenreigen", "IT": "Danzadipiume", "RU": "Танец с Перьями", "ES": "Danza Pluma" }, + "pokemon_move_349": { + "EN": "Fiery Dance", + "RU": "Горячий Танец" + }, + "pokemon_move_350": { + "PT-BR": "Vento de Fada", + "EN": "Fairy Wind", + "FR": "Vent Féérique", + "DE": "Feenbrise", + "IT": "Vento di Fata", + "RU": "Ветер Фей", + "ES": "Viento Feérico" + }, + "pokemon_move_351": { + "EN": "Relic Song", + "RU": "Древняя Песня" + }, "pokemon_move_352": { "PT-BR": "Esfera Climática", "EN": "Weather Ball", - "FI": "Weather Ball", - "NL": "Weather Ball", - "NO": "Weather Ball", - "PL": "Weather Ball", "FR": "Ball’Météo", "DE": "Meteorologe", "IT": "Palla Clima", @@ -3720,11 +2572,7 @@ "pokemon_move_353": { "PT-BR": "Caninos Psíquicos", "EN": "Psychic Fangs", - "FI": "Psychic Fangs", - "NL": "Psychic Fangs", - "NO": "Psychic Fangs", - "PL": "Psychic Fangs", - "FR": "Psychic Fangs", + "FR": "Psycho-Croc", "DE": "Psychobeißer", "IT": "Psicozanna", "RU": "Психоклыки", @@ -3733,10 +2581,6 @@ "pokemon_move_354": { "PT-BR": "Fúria de Hiperespaço", "EN": "Hyperspace Fury", - "FI": "Hyperspace Fury", - "NL": "Hyperspace Fury", - "NO": "Hyperspace Fury", - "PL": "Hyperspace Fury", "FR": "Furie Dimension", "DE": "Dimensionswahn", "IT": "Urtodimensionale", @@ -3746,23 +2590,24 @@ "pokemon_move_355": { "PT-BR": "Fenda de Hiperespaço", "EN": "Hyperspace Hole", - "FI": "Hyperspace Hole", - "NL": "Hyperspace Hole", - "NO": "Hyperspace Hole", - "PL": "Hyperspace Hole", "FR": "TrouDimensionnel", "DE": "Dimensionsloch", "IT": "Forodimensionale", "RU": "Гиперпространственная Дыра", "ES": "Paso Dimensional" }, + "pokemon_move_356": { + "PT-BR": "Chute Duplo", + "EN": "Double Kick", + "FR": "Double Pied", + "DE": "Doppelkick", + "IT": "Doppiocalcio", + "RU": "Двойной Пинок", + "ES": "Doble Patada" + }, "pokemon_move_357": { "PT-BR": "Folha Mágica", "EN": "Magical Leaf", - "FI": "Magical Leaf", - "NL": "Magical Leaf", - "NO": "Magical Leaf", - "PL": "Magical Leaf", "FR": "Feuille Magik", "DE": "Zauberblatt", "IT": "Fogliamagica", @@ -3772,10 +2617,6 @@ "pokemon_move_358": { "PT-BR": "Fogo Sagrado", "EN": "Sacred Fire", - "FI": "Sacred Fire", - "NL": "Sacred Fire", - "NO": "Sacred Fire", - "PL": "Sacred Fire", "FR": "Feu Sacré", "DE": "Läuterfeuer", "IT": "Magifuoco", @@ -3785,10 +2626,6 @@ "pokemon_move_359": { "PT-BR": "Lança Congelada", "EN": "Icicle Spear", - "FI": "Icicle Spear", - "NL": "Icicle Spear", - "NO": "Icicle Spear", - "PL": "Icicle Spear", "FR": "Stalactite", "DE": "Eisspeer", "IT": "Gelolancia", @@ -3798,10 +2635,6 @@ "pokemon_move_360": { "PT-BR": "Explosão Aérea+", "EN": "Aeroblast+", - "FI": "Aeroblast+", - "NL": "Aeroblast+", - "NO": "Aeroblast+", - "PL": "Aeroblast+", "FR": "Aéroblast+", "DE": "Luftstoß+", "IT": "Aerocolpo+", @@ -3811,10 +2644,6 @@ "pokemon_move_361": { "PT-BR": "Explosão Aérea++", "EN": "Aeroblast++", - "FI": "Aeroblast++", - "NL": "Aeroblast++", - "NO": "Aeroblast++", - "PL": "Aeroblast++", "FR": "Aéroblast++", "DE": "Luftstoß++", "IT": "Aerocolpo++", @@ -3824,10 +2653,6 @@ "pokemon_move_362": { "PT-BR": "Fogo Sagrado+", "EN": "Sacred Fire+", - "FI": "Sacred Fire+", - "NL": "Sacred Fire+", - "NO": "Sacred Fire+", - "PL": "Sacred Fire+", "FR": "Feu Sacré+", "DE": "Läuterfeuer+", "IT": "Magifuoco+", @@ -3837,10 +2662,6 @@ "pokemon_move_363": { "PT-BR": "Fogo Sagrado++", "EN": "Sacred Fire++", - "FI": "Sacred Fire++", - "NL": "Sacred Fire++", - "NO": "Sacred Fire++", - "PL": "Sacred Fire++", "FR": "Feu Sacré++", "DE": "Läuterfeuer++", "IT": "Magifuoco++", @@ -3848,16 +2669,1149 @@ "ES": "Fuego Sagrado++" }, "pokemon_move_364": { - "PT-BR": "Acrobatics", + "PT-BR": "Acrobático", "EN": "Acrobatics", - "FI": "Acrobatics", - "NL": "Acrobatics", - "NO": "Acrobatics", - "PL": "Acrobatics", - "FR": "Acrobatics", - "DE": "Acrobatics", - "IT": "Acrobatics", + "FR": "Acrobatie", + "DE": "Akrobatik", + "IT": "Acrobazia", "RU": "Акробатика", - "ES": "Acrobatics" + "ES": "Acróbata" + }, + "pokemon_move_365": { + "PT-BR": "Purga de Esplendor", + "EN": "Luster Purge", + "FR": "Lumi-Éclat", + "DE": "Scheinwerfer", + "IT": "Abbagliante", + "RU": "Генеральная Чистка", + "ES": "Resplandor" + }, + "pokemon_move_366": { + "PT-BR": "Bola de Névoa", + "EN": "Mist Ball", + "FR": "Ball’Brume", + "DE": "Nebelball", + "IT": "Foschisfera", + "RU": "Шар Тумана", + "ES": "Bola Neblina" + }, + "pokemon_move_367": { + "PT-BR": "Balanço Violento", + "EN": "Brutal Swing", + "FR": "Centrifugifle", + "DE": "Wirbler", + "IT": "Vorticolpo", + "RU": "Резкий Поворот", + "ES": "Giro Vil" + }, + "pokemon_move_368": { + "PT-BR": "Rolagem", + "EN": "Rollout", + "FR": "Roulade", + "DE": "Walzer", + "IT": "Rotolamento", + "RU": "Перекат", + "ES": "Desenrollar" + }, + "pokemon_move_369": { + "PT-BR": "Semente Ofuscante", + "EN": "Seed Flare", + "FR": "Fulmigraine", + "DE": "Schocksamen", + "IT": "Infuriaseme", + "RU": "Сияние Семени", + "ES": "Fogonazo" + }, + "pokemon_move_370": { + "PT-BR": "Obstruir", + "EN": "Obstruct", + "FR": "Blocage", + "DE": "Abblocker", + "IT": "Sbarramento", + "RU": "Преграда", + "ES": "Obstrucción" + }, + "pokemon_move_371": { + "PT-BR": "Força das Sombras", + "EN": "Shadow Force", + "FR": "Revenant", + "DE": "Schemenkraft", + "IT": "Oscurotuffo", + "RU": "Сумеречная Сила", + "ES": "Golpe Umbrío" + }, + "pokemon_move_372": { + "PT-BR": "Raio Meteórico", + "EN": "Meteor Beam", + "FR": "Laser Météore", + "DE": "Meteorstrahl", + "IT": "Raggiometeora", + "RU": "Метеоритный Луч", + "ES": "Rayo Meteórico" + }, + "pokemon_move_373": { + "PT-BR": "Estrela Ninja de Água", + "EN": "Water Shuriken", + "FR": "Sheauriken", + "DE": "Wasser-Shuriken", + "IT": "Acqualame", + "RU": "Водяной Сюрикен", + "ES": "Shuriken de Agua" + }, + "pokemon_move_374": { + "PT-BR": "Raio de Fusão", + "EN": "Fusion Bolt", + "FR": "Éclair Croix", + "DE": "Kreuzdonner", + "IT": "Incrotuono", + "RU": "Термоядерный Разряд", + "ES": "Rayo Fusión" + }, + "pokemon_move_375": { + "PT-BR": "Chama da Fusão", + "EN": "Fusion Flare", + "FR": "Flamme Croix", + "DE": "Kreuzflamme", + "IT": "Incrofiamma", + "RU": "Ядерная Вспышка", + "ES": "Llama Fusión" + }, + "pokemon_move_376": { + "EN": "Poltergeist", + "FR": "Esprit Frappeur", + "RU": "Полтергейст" + }, + "pokemon_move_377": { + "PT-BR": "Potência Equina", + "EN": "High Horsepower", + "FR": "Cavalerie Lourde", + "DE": "Pferdestärke", + "IT": "Forza Equina", + "RU": "Лошадиная Сила", + "ES": "Fuerza Equina" + }, + "pokemon_move_378": { + "PT-BR": "Glaciar", + "EN": "Glaciate", + "FR": "Ère Glaciaire", + "DE": "Eiszeit", + "IT": "Gelamondo", + "RU": "Замораживание", + "ES": "Mundo Gélido" + }, + "pokemon_move_379": { + "PT-BR": "Golpe Deslizante", + "EN": "Breaking Swipe", + "FR": "Abattage", + "DE": "Breitseite", + "IT": "Vastoimpatto", + "RU": "Разбивающий Удар", + "ES": "Vasto Impacto" + }, + "pokemon_move_380": { + "PT-BR": "Rajada Explosiva", + "EN": "Boomburst", + "FR": "Bang Sonique", + "DE": "Überschallknall", + "IT": "Ondaboato", + "RU": "Громкий Взрыв", + "ES": "Estruendo" + }, + "pokemon_move_381": { + "PT-BR": "Pancada de Ferro Dupla", + "EN": "Double Iron Bash", + "FR": "Écrous d’Poing", + "DE": "Panzerfäuste", + "IT": "Pugni Corazzati", + "RU": "Двойной Стальной Таран", + "ES": "Ferropuño Doble" + }, + "pokemon_move_382": { + "PT-BR": "Fogo Místico", + "EN": "Mystical Fire", + "FR": "Feu Ensorcelé", + "DE": "Magieflamme", + "IT": "Magifiamma", + "RU": "Мистический Огонь", + "ES": "Llama Embrujada" + }, + "pokemon_move_383": { + "PT-BR": "Aquaríete", + "EN": "Liquidation", + "FR": "Aqua-Brèche", + "DE": "Aquadurchstoß", + "IT": "Idrobreccia", + "RU": "Ликвидация", + "ES": "Hidroariete" + }, + "pokemon_move_384": { + "PT-BR": "Ascenção do Dragão", + "EN": "Dragon Ascent", + "FR": "Draco Ascension", + "DE": "Zenitstürmer", + "IT": "Ascesa del Drago", + "RU": "Взлёт Дракона", + "ES": "Ascenso Draco" + }, + "pokemon_move_385": { + "PT-BR": "Folhagem", + "EN": "Leafage", + "FR": "Feuillage", + "DE": "Blattwerk", + "IT": "Fogliame", + "RU": "Облиствление", + "ES": "Follaje" + }, + "pokemon_move_386": { + "PT-BR": "Tempestade de Magma", + "EN": "Magma Storm", + "FR": "Vortex Magma", + "DE": "Lavasturm", + "IT": "Magmaclisma", + "RU": "Магма-Буря", + "ES": "Lluvia Ígnea" + }, + "pokemon_move_387": { + "PT-BR": "Geomancia", + "EN": "Geomancy", + "FR": "Géo-Contrôle", + "DE": "Geokontrolle", + "IT": "Geocontrollo", + "RU": "Геомантия", + "ES": "Geocontrol" + }, + "pokemon_move_388": { + "PT-BR": "Laceração Espacial", + "EN": "Spacial Rend", + "FR": "Spatio-Rift", + "DE": "Raumschlag", + "IT": "Fendispazio", + "RU": "Пространственный Разрыв", + "ES": "Corte Vacío" + }, + "pokemon_move_389": { + "PT-BR": "Asa do Esquecimento", + "EN": "Oblivion Wing", + "FR": "Mort-Ailes", + "DE": "Unheilsschwingen", + "IT": "Ali del Fato", + "RU": "Крылья Забвения", + "ES": "Ala Mortífera" + }, + "pokemon_move_390": { + "PT-BR": "Ira da Natureza", + "EN": "Nature’s Madness", + "FR": "Ire de la Nature", + "DE": "Naturzorn", + "IT": "Ira della Natura", + "RU": "Безумие Природы", + "ES": "Furia Natural" + }, + "pokemon_move_391": { + "PT-BR": "Pinote Triplo", + "EN": "Triple Axel", + "DE": "Dreifach-Axel", + "IT": "Triplo Axel", + "RU": "Тройной Аксель" + }, + "pokemon_move_392": { + "PT-BR": "Desbravar", + "EN": "Trailblaze", + "FR": "Désherbaffe", + "DE": "Wegbereiter", + "IT": "Apripista", + "RU": "Новый Путь", + "ES": "Abrecaminos" + }, + "pokemon_move_393": { + "PT-BR": "Areias Ardentes", + "EN": "Scorching Sands", + "FR": "Sable Ardent", + "DE": "Brandsand", + "IT": "Sabbiardente", + "RU": "Раскалённый Песок", + "ES": "Arenas Ardientes" + }, + "pokemon_move_394": { + "PT-BR": "Rugido do Tempo", + "EN": "Roar of Time", + "FR": "Hurle-Temps", + "DE": "Zeitenlärm", + "IT": "Fragortempo", + "RU": "Рёв Времени", + "ES": "Distorsión" + }, + "pokemon_move_395": { + "PT-BR": "Tempestade Álgida", + "EN": "Bleakwind Storm", + "FR": "Typhon Hivernal", + "DE": "Polarorkan", + "IT": "Tempesta Boreale", + "RU": "Ледяная Буря", + "ES": "Vendaval Gélido" + }, + "pokemon_move_396": { + "PT-BR": "Tempestade Saibrosa", + "EN": "Sandsear Storm", + "FR": "Typhon Pyrosable", + "DE": "Wüstenorkan", + "IT": "Tempesta Ardente", + "RU": "Обжигающая Буря", + "ES": "Simún de Arena" + }, + "pokemon_move_397": { + "PT-BR": "Tempestade Chocante", + "EN": "Wildbolt Storm", + "FR": "Typhon Fulgurant", + "DE": "Donnerorkan", + "IT": "Tempesta Tonante", + "RU": "Громовая Буря", + "ES": "Electormenta" + }, + "pokemon_move_398": { + "PT-BR": "Manilha Sombria", + "EN": "Spirit Shackle", + "FR": "Tisse Ombre", + "DE": "Schattenfessel", + "IT": "Cucitura d’Ombra", + "RU": "Спиритические Оковы", + "ES": "Puntada Sombría" + }, + "pokemon_move_399": { + "PT-BR": "Investida Trovão", + "EN": "Volt Tackle", + "FR": "Électacle", + "DE": "Volttackle", + "IT": "Locomovolt", + "RU": "Вольт-Бросок", + "ES": "Placaje Eléctrico" + }, + "pokemon_move_400": { + "PT-BR": "Lariat Escuro", + "EN": "Darkest Lariat", + "FR": "Dark Lariat", + "DE": "Dark Lariat", + "IT": "Braccioteso", + "RU": "Тёмное Лассо", + "ES": "Lariat Oscuro" + }, + "pokemon_move_401": { + "PT-BR": "Onda Psíquica", + "EN": "Psywave", + "FR": "Vague Psy", + "DE": "Psywelle", + "IT": "Psiconda", + "RU": "Психоволны", + "ES": "Psicoonda" + }, + "pokemon_move_402": { + "PT-BR": "Som de Metal", + "EN": "Metal Sound", + "FR": "Strido-Son", + "DE": "Metallsound", + "IT": "Ferrostrido", + "RU": "Металлический Звук", + "ES": "Eco Metálico" + }, + "pokemon_move_403": { + "PT-BR": "Ataque de Areia", + "EN": "Sand Attack", + "FR": "Jet de Sable", + "DE": "Sandwirbel", + "IT": "Turbosabbia", + "RU": "Песчаная Атака", + "ES": "Ataque Arena" + }, + "pokemon_move_404": { + "PT-BR": "Ataque Solaraço", + "EN": "Sunsteel Strike", + "FR": "Choc Météore", + "DE": "Stahlgestirn", + "IT": "Astrocarica", + "RU": "Солнечно-Стальной Удар", + "ES": "Meteoimpacto" + }, + "pokemon_move_405": { + "PT-BR": "Feixe Espectral", + "EN": "Moongeist Beam", + "FR": "Rayon Spectral", + "DE": "Schattenstrahl", + "IT": "Raggio d’Ombra", + "RU": "Луч Лунного Духа", + "ES": "Rayo Umbrío" + }, + "pokemon_move_406": { + "PT-BR": "Roda de Aura", + "EN": "Aura Wheel", + "FR": "Roue Libre", + "DE": "Aura-Rad", + "IT": "Ruota d’Aura", + "RU": "Колесо Ауры", + "ES": "Rueda Aural" + }, + "pokemon_move_407": { + "PT-BR": "Roda de Aura", + "EN": "Aura Wheel", + "FR": "Roue Libre", + "DE": "Aura-Rad", + "IT": "Ruota d’Aura", + "RU": "Колесо Ауры", + "ES": "Rueda Aural" + }, + "pokemon_move_408": { + "PT-BR": "Chute de Pulo Alto", + "EN": "High Jump Kick", + "FR": "Pied Voltige", + "DE": "Turmkick", + "IT": "Calcinvolo", + "RU": "Прыжок-Удар", + "ES": "Patada Salto Alta" + }, + "pokemon_move_409": { + "PT-BR": "Chama Max", + "EN": "Max Flare", + "FR": "Pyromax", + "DE": "Dyna-Brand", + "IT": "Dynafiammata", + "RU": "Вспышка Макс", + "ES": "Maxignición" + }, + "pokemon_move_410": { + "PT-BR": "Nuvem de Inseto Max", + "EN": "Max Flutterby", + "FR": "Insectomax", + "DE": "Dyna-Schwarm", + "IT": "Dynainsetto", + "RU": "Бабочка Макс", + "ES": "Maxinsecto" + }, + "pokemon_move_411": { + "PT-BR": "Raio Max", + "EN": "Max Lightning", + "FR": "Fulguromax", + "DE": "Dyna-Gewitter", + "IT": "Dynasaetta", + "RU": "Молния Макс", + "ES": "Maxitormenta" + }, + "pokemon_move_412": { + "PT-BR": "Golpe Max", + "EN": "Max Strike", + "FR": "Normalomax", + "DE": "Dyna-Angriff", + "IT": "Dynattacco", + "RU": "Удар Макс", + "ES": "Maxiataque" + }, + "pokemon_move_413": { + "PT-BR": "Punho Max", + "EN": "Max Knuckle", + "FR": "Pugilomax", + "DE": "Dyna-Faust", + "IT": "Dynapugno", + "RU": "Кулак Макс", + "ES": "Maxipuño" + }, + "pokemon_move_414": { + "PT-BR": "Espectro Max", + "EN": "Max Phantasm", + "FR": "Spectromax", + "DE": "Dyna-Spuk", + "IT": "Dynavuoto", + "RU": "Иллюзия Макс", + "ES": "Maxiespectro" + }, + "pokemon_move_415": { + "PT-BR": "Temporal de Granizo Max", + "EN": "Max Hailstorm", + "FR": "Cryomax", + "DE": "Dyna-Frost", + "IT": "Dynagelo", + "RU": "Град Макс", + "ES": "Maxihelada" + }, + "pokemon_move_416": { + "PT-BR": "Corrosão Max", + "EN": "Max Ooze", + "FR": "Toxinomax", + "DE": "Dyna-Giftschwall", + "IT": "Dynacorrosione", + "RU": "Токсины Макс", + "ES": "Maxiácido" + }, + "pokemon_move_417": { + "PT-BR": "Gêiser Max", + "EN": "Max Geyser", + "FR": "Hydromax", + "DE": "Dyna-Flut", + "IT": "Dynaflusso", + "RU": "Гейзер Макс", + "ES": "Maxichorro" + }, + "pokemon_move_418": { + "PT-BR": "Corrente de Ar Max", + "EN": "Max Airstream", + "FR": "Aéromax", + "DE": "Dyna-Düse", + "IT": "Dynajet", + "RU": "Вихрь Макс", + "ES": "Maxiciclón" + }, + "pokemon_move_419": { + "PT-BR": "Queda Estelar Max", + "EN": "Max Starfall", + "FR": "Enchantomax", + "DE": "Dyna-Zauber", + "IT": "Dynafata", + "RU": "Волшебство Макс", + "ES": "Maxiestela" + }, + "pokemon_move_420": { + "PT-BR": "Bafo Dracônico Max", + "EN": "Max Wyrmwind", + "FR": "Dracomax", + "DE": "Dyna-Wyrm", + "IT": "Dynadragone", + "RU": "Дракон Макс", + "ES": "Maxidraco" + }, + "pokemon_move_421": { + "PT-BR": "Tempestade Mental Max", + "EN": "Max Mindstorm", + "FR": "Psychomax", + "DE": "Dyna-Kinese", + "IT": "Dynapsiche", + "RU": "Психокинез Макс", + "ES": "Maxionda" + }, + "pokemon_move_422": { + "PT-BR": "Desabamento Max", + "EN": "Max Rockfall", + "FR": "Lithomax", + "DE": "Dyna-Brocken", + "IT": "Dynamacigno", + "RU": "Камнепад Макс", + "ES": "Maxilito" + }, + "pokemon_move_423": { + "PT-BR": "Oscilação Max", + "EN": "Max Quake", + "FR": "Sismomax", + "DE": "Dyna-Erdstoß", + "IT": "Dynasisma", + "RU": "Землетрясение Макс", + "ES": "Maxitemblor" + }, + "pokemon_move_424": { + "PT-BR": "Escuridão Max", + "EN": "Max Darkness", + "FR": "Sinistromax", + "DE": "Dyna-Dunkel", + "IT": "Dynatenebre", + "RU": "Темнота Макс", + "ES": "Maxisombra" + }, + "pokemon_move_425": { + "PT-BR": "Flora Max", + "EN": "Max Overgrowth", + "FR": "Phytomax", + "DE": "Dyna-Flora", + "IT": "Dynaflora", + "RU": "Рост Макс", + "ES": "Maxiflora" + }, + "pokemon_move_426": { + "PT-BR": "Espinho de Aço Max", + "EN": "Max Steelspike", + "FR": "Métallomax", + "DE": "Dyna-Stahlzacken", + "IT": "Dynametallo", + "RU": "Остриё Макс", + "ES": "Maximetal" + }, + "pokemon_move_427": { + "PT-BR": "Queimada G-Max", + "EN": "G-Max Wildfire", + "FR": "Fournaise G-Max", + "DE": "Giga-Feuerflug", + "IT": "Gigavampa", + "RU": "Лесной Пожар G-Макс", + "ES": "Gigallamarada" + }, + "pokemon_move_428": { + "PT-BR": "Perturbação G-Max", + "EN": "G-Max Befuddle", + "FR": "Illusion G-Max", + "DE": "Giga-Benebelung", + "IT": "Gigastupore", + "RU": "Одурманивание G-Макс", + "ES": "Gigaestupor" + }, + "pokemon_move_429": { + "PT-BR": "Trovoada G-Max", + "EN": "G-Max Volt Crash", + "FR": "Foudre G-Max", + "DE": "Giga-Blitzhagel", + "IT": "Gigapikafolgori", + "RU": "Вольт-Таран G-Макс", + "ES": "Gigatronada" + }, + "pokemon_move_430": { + "PT-BR": "Corrida do Ouro G-Max", + "EN": "G-Max Gold Rush", + "FR": "Pactole G-Max", + "DE": "Giga-Münzregen", + "IT": "Gigamonete", + "RU": "Золотая Лихорадка G-Макс", + "ES": "Gigamonedas" + }, + "pokemon_move_431": { + "PT-BR": "Golpe Chi G-Max", + "EN": "G-Max Chi Strike", + "FR": "Frappe G-Max", + "DE": "Giga-Fokusschlag", + "IT": "Gigapugnointuito", + "RU": "Кулак G-Макс", + "ES": "Gigapuñición" + }, + "pokemon_move_432": { + "PT-BR": "Terror G-Max", + "EN": "G-Max Terror", + "FR": "Hantise G-Max", + "DE": "Giga-Spuksperre", + "IT": "Gigaillusione", + "RU": "Ужас G-Макс", + "ES": "Gigaaparición" + }, + "pokemon_move_433": { + "PT-BR": "Explosão de Espuma G-Max", + "EN": "G-Max Foam Burst", + "FR": "Bulles G-Max", + "DE": "Giga-Schaumbad", + "IT": "Gigaschiuma", + "RU": "Пена G-Макс", + "ES": "Gigaespuma" + }, + "pokemon_move_434": { + "PT-BR": "Ressonância G-Max", + "EN": "G-Max Resonance", + "FR": "Résonance G-Max", + "DE": "Giga-Melodie", + "IT": "Gigamelodia", + "RU": "Мелодия G-Макс", + "ES": "Gigamelodía" + }, + "pokemon_move_435": { + "PT-BR": "Ternura G-Max", + "EN": "G-Max Cuddle", + "FR": "Câlin G-Max", + "DE": "Giga-Gekuschel", + "IT": "Gigabbraccio", + "RU": "Обнимашки G-Макс", + "ES": "Gigaternura" + }, + "pokemon_move_436": { + "PT-BR": "Reabastecer G-Max", + "EN": "G-Max Replenish", + "FR": "Récolte G-Max", + "DE": "Giga-Recycling", + "IT": "Gigarinnovamento", + "RU": "Пополнение Запаса G-Макс", + "ES": "Gigarreciclaje" + }, + "pokemon_move_437": { + "PT-BR": "Pestilência G-Max", + "EN": "G-Max Malodor", + "FR": "Pestilence G-Max", + "DE": "Giga-Gestank", + "IT": "Gigafetore", + "RU": "Зловоние G-Макс", + "ES": "Gigapestilencia" + }, + "pokemon_move_438": { + "PT-BR": "Derretimento G-Max", + "EN": "G-Max Meltdown", + "FR": "Fonte G-Max", + "DE": "Giga-Schmelze", + "IT": "Gigaliquefazione", + "RU": "Разрушение G-Макс", + "ES": "Gigafundido" + }, + "pokemon_move_439": { + "PT-BR": "Intempérie G-Max", + "EN": "G-Max Wind Rage", + "FR": "Rafale G-Max", + "DE": "Giga-Sturmstoß", + "IT": "Gigaciclone", + "RU": "Ярость Ветра G-Макс", + "ES": "Gigahuracán" + }, + "pokemon_move_440": { + "PT-BR": "Seriedade G-Max", + "EN": "G-Max Gravitas", + "FR": "Ondes G-Max", + "DE": "Giga-Astrowellen", + "IT": "Gigagravitoforza", + "RU": "Сила Тяжести G-Макс", + "ES": "Gigabóveda" + }, + "pokemon_move_441": { + "PT-BR": "Enxurrada de Rochas", + "EN": "G-Max Stonesurge", + "FR": "Récif G-Max", + "DE": "Giga-Geröll", + "IT": "Gigarocciagetto", + "RU": "Каменный Импульс G-Макс", + "ES": "Gigatrampa Rocas" + }, + "pokemon_move_442": { + "PT-BR": "Rocha Vulcânica G-Max", + "EN": "G-Max Volcalith", + "FR": "Téphra G-Max", + "DE": "Giga-Schlacke", + "IT": "Gigalapilli", + "RU": "Вулкалит G-Макс", + "ES": "Gigarroca Ígnea" + }, + "pokemon_move_443": { + "PT-BR": "Acidez G-Max", + "EN": "G-Max Tartness", + "FR": "Corrosion G-Max", + "DE": "Giga-Säureguss", + "IT": "Gigattaccoacido", + "RU": "Кислотность G-Макс", + "ES": "Gigacorrosión" + }, + "pokemon_move_444": { + "PT-BR": "Néctar G-Max", + "EN": "G-Max Sweetness", + "FR": "Nectar G-Max", + "DE": "Giga-Nektarflut", + "IT": "Gigambrosia", + "RU": "Сладость G-Макс", + "ES": "Giganéctar" + }, + "pokemon_move_445": { + "PT-BR": "Jorro de Areia G-Max", + "EN": "G-Max Sandblast", + "FR": "Enlisement G-Max", + "DE": "Giga-Sandstoß", + "IT": "Gigavortisabbia", + "RU": "Песчаная Струя G-Макс", + "ES": "Gigapolvareda" + }, + "pokemon_move_446": { + "PT-BR": "Eletrocutar G-Max", + "EN": "G-Max Stun Shock", + "FR": "Choc G-Max", + "DE": "Giga-Voltschlag", + "IT": "Gigatoxiscossa", + "RU": "Ошеломление G-Макс", + "ES": "Gigadescarga" + }, + "pokemon_move_447": { + "PT-BR": "Centiferno G-Max", + "EN": "G-Max Centiferno", + "FR": "Combustion G-Max", + "DE": "Giga-Feuerkessel", + "IT": "Gigamillefiamme", + "RU": "Сентиферно G-Макс", + "ES": "Gigacienfuegos" + }, + "pokemon_move_448": { + "PT-BR": "Castigo G-Max", + "EN": "G-Max Smite", + "FR": "Sentence G-Max", + "DE": "Giga-Sanktion", + "IT": "Gigacastigo", + "RU": "Наказание G-Макс", + "ES": "Gigacastigo" + }, + "pokemon_move_449": { + "PT-BR": "Cochilo G-Max", + "EN": "G-Max Snooze", + "FR": "Torpeur G-Max", + "DE": "Giga-Gähnzwang", + "IT": "Gigatorpore", + "RU": "Дрёма G-Макс", + "ES": "Gigasopor" + }, + "pokemon_move_450": { + "PT-BR": "Grande Final G-Max", + "EN": "G-Max Finale", + "FR": "Cure G-Max", + "DE": "Giga-Lichtblick", + "IT": "Gigagranfinale", + "RU": "Финал G-Макс", + "ES": "Gigacolofón" + }, + "pokemon_move_451": { + "PT-BR": "Enxurrada de Aço G-Max", + "EN": "G-Max Steelsurge", + "FR": "Percée G-Max", + "DE": "Giga-Stahlschlag", + "IT": "Gigaferroaculei", + "RU": "Стальные Шипы G-Макс", + "ES": "Gigatrampa Acero" + }, + "pokemon_move_452": { + "PT-BR": "Esgotamento G-Max", + "EN": "G-Max Depletion", + "FR": "Usure G-Max", + "DE": "Giga-Dämpfer", + "IT": "Gigalogoramento", + "RU": "Опустошение G-Макс", + "ES": "Gigadesgaste" + }, + "pokemon_move_453": { + "PT-BR": "Chicotada G-Max", + "EN": "G-Max Vine Lash", + "FR": "Fouet G-Max", + "DE": "Giga-Geißel", + "IT": "Gigasferzata", + "RU": "Удар Лозами G-Макс", + "ES": "Gigalianas" + }, + "pokemon_move_454": { + "PT-BR": "Bombardeamento G-Max", + "EN": "G-Max Cannonade", + "FR": "Canonnade G-Max", + "DE": "Giga-Beschuss", + "IT": "Gigacannonata", + "RU": "Канонада G-Макс", + "ES": "Gigacañonazo" + }, + "pokemon_move_455": { + "PT-BR": "Solo de Tambores G-Max", + "EN": "G-Max Drum Solo", + "FR": "Percussion G-Max", + "DE": "Giga-Getrommel", + "IT": "Gigarullio", + "RU": "Соло на Барабанах G-Макс", + "ES": "Gigarredoble" + }, + "pokemon_move_456": { + "PT-BR": "Bola de Chamas G-Max", + "EN": "G-Max Fireball", + "FR": "Pyroball G-Max", + "DE": "Giga-Brandball", + "IT": "Gigafiammopalla", + "RU": "Огненный Шар G-Макс", + "ES": "Gigaesfera Ígnea" + }, + "pokemon_move_457": { + "PT-BR": "Hidroatirador G-Max", + "EN": "G-Max Hydrosnipe", + "FR": "Gâchette G-Max", + "DE": "Giga-Schütze", + "IT": "Gigasparomirato", + "RU": "Гидроснайпер G-Макс", + "ES": "Gigadisparo" + }, + "pokemon_move_458": { + "PT-BR": "Golpe Único G-Max", + "EN": "G-Max One Blow", + "FR": "Coup Final G-Max", + "DE": "Giga-Einzelhieb", + "IT": "Gigasingolcolpo", + "RU": "Единый Удар G-Макс", + "ES": "Gigagolpe Brusco" + }, + "pokemon_move_459": { + "PT-BR": "Fluxo Veloz G-Max", + "EN": "G-Max Rapid Flow", + "FR": "Multicoup G-Max", + "DE": "Giga-Multihieb", + "IT": "Gigapluricolpo", + "RU": "Серийный Поток G-Макс", + "ES": "Gigagolpe Fluido" + }, + "pokemon_move_460": { + "EN": "Vn Bm 052" + }, + "pokemon_move_461": { + "EN": "Vn Bm 053" + }, + "pokemon_move_462": { + "PT-BR": "Palma da Força", + "EN": "Force Palm", + "FR": "Forte-Paume", + "DE": "Kraftwelle", + "IT": "Palmoforza", + "RU": "Сильная Ладонь", + "ES": "Palmeo" + }, + "pokemon_move_463": { + "PT-BR": "Aria Cintilante", + "EN": "Sparkling Aria", + "FR": "Aria de l’Écume", + "DE": "Schaumserenade", + "IT": "Canto Effimero", + "RU": "Искрящаяся Ария", + "ES": "Aria Burbuja" + }, + "pokemon_move_464": { + "PT-BR": "Punho Feroz", + "EN": "Rage Fist", + "FR": "Poing de Colère", + "DE": "Zornesfaust", + "IT": "Pugno Furibondo", + "RU": "Кулак Ярости", + "ES": "Puño Furia" + }, + "pokemon_move_465": { + "PT-BR": "Truque Floral", + "EN": "Flower Trick", + "FR": "Magie Florale", + "DE": "Blumentrick", + "IT": "Prestigiafiore", + "RU": "Цветочный Фокус", + "ES": "Truco Floral" + }, + "pokemon_move_466": { + "PT-BR": "Choque Congelante", + "EN": "Freeze Shock", + "FR": "Éclair Gelé", + "DE": "Frostvolt", + "IT": "Elettrogelo", + "RU": "Холодовой Удар", + "ES": "Rayo Gélido" + }, + "pokemon_move_467": { + "PT-BR": "Queimadura de Gelo", + "EN": "Ice Burn", + "FR": "Feu Glacé", + "DE": "Frosthauch", + "IT": "Vampagelida", + "RU": "Ледяной Ожог", + "ES": "Llama Gélida" + }, + "pokemon_move_468": { + "PT-BR": "Canção Ardente", + "EN": "Torch Song", + "FR": "Chant Flamboyant", + "DE": "Loderlied", + "IT": "Canzone Ardente", + "RU": "Пение Пламени", + "ES": "Canto Ardiente" + }, + "pokemon_move_469": { + "PT-BR": "Espada Colossal", + "EN": "Behemoth Blade", + "FR": "Gladius Maximus", + "DE": "Gigantenhieb", + "IT": "Taglio Maestoso", + "RU": "Меч Левиафана", + "ES": "Tajo Supremo" + }, + "pokemon_move_470": { + "PT-BR": "Pancada Colossal", + "EN": "Behemoth Bash", + "FR": "Aegis Maxima", + "DE": "Gigantenstoß", + "IT": "Colpo Maestoso", + "RU": "Удар Левиафана", + "ES": "Embate Supremo" + }, + "pokemon_move_471": { + "PT-BR": "Supermão", + "EN": "Upper Hand", + "FR": "Prio-Parade", + "DE": "Schnellkonter", + "IT": "Colpo di Mano", + "RU": "Ручной Выстрел", + "ES": "Palma Rauda" + }, + "pokemon_move_472": { + "PT-BR": "Prisão Elétrica", + "EN": "Thunder Cage", + "FR": "Voltageôle", + "DE": "Blitzgefängnis", + "IT": "Elettrogabbia", + "RU": "Громовая Клетка", + "ES": "Electrojaula" + }, + "pokemon_move_473": { + "PT-BR": "Indefeso", + "EN": "Guard", + "FR": "Garde", + "DE": "Wall", + "IT": "Barriera", + "RU": "Защита", + "ES": "Barrera" + }, + "pokemon_move_474": { + "PT-BR": "Espírito", + "EN": "Spirit", + "FR": "Régé", + "DE": "Kampfgeist", + "IT": "Spirito", + "RU": "Дух", + "ES": "Espíritu" + }, + "pokemon_move_475": { + "PT-BR": "Indefeso", + "EN": "Guard", + "FR": "Garde", + "DE": "Wall", + "IT": "Barriera", + "RU": "Защита", + "ES": "Barrera" + }, + "pokemon_move_476": { + "PT-BR": "Espírito", + "EN": "Spirit", + "FR": "Régé", + "DE": "Kampfgeist", + "IT": "Spirito", + "RU": "Дух", + "ES": "Espíritu" + }, + "pokemon_move_477": { + "PT-BR": "Indefeso", + "EN": "Guard", + "FR": "Garde", + "DE": "Wall", + "IT": "Barriera", + "RU": "Защита", + "ES": "Barrera" + }, + "pokemon_move_478": { + "PT-BR": "Espírito", + "EN": "Spirit", + "FR": "Régé", + "DE": "Kampfgeist", + "IT": "Spirito", + "RU": "Дух", + "ES": "Espíritu" + }, + "pokemon_move_479": { + "PT-BR": "Espada Colossal", + "EN": "Behemoth Blade", + "FR": "Gladius Maximus", + "DE": "Gigantenhieb", + "IT": "Taglio Maestoso", + "RU": "Меч Левиафана", + "ES": "Tajo Supremo" + }, + "pokemon_move_480": { + "PT-BR": "Pancada Colossal", + "EN": "Behemoth Bash", + "FR": "Aegis Maxima", + "DE": "Gigantenstoß", + "IT": "Colpo Maestoso", + "RU": "Удар Левиафана", + "ES": "Embate Supremo" + }, + "pokemon_move_481": { + "EN": "Thunder Cage Fast" + }, + "pokemon_move_482": { + "PT-BR": "Canhão Dinamax", + "EN": "Dynamax Cannon", + "FR": "Canon Dynamax", + "DE": "Dynamax-Kanone", + "IT": "Cannone Dynamax", + "RU": "Динамакс-Пушка", + "ES": "Cañón Dinamax" + }, + "pokemon_move_483": { + "PT-BR": "Canhão Dinamax", + "EN": "Dynamax Cannon", + "FR": "Canon Dynamax", + "DE": "Dynamax-Kanone", + "IT": "Cannone Dynamax", + "RU": "Динамакс-Пушка", + "ES": "Cañón Dinamax" + }, + "pokemon_move_484": { + "PT-BR": "Batida de Escamas", + "EN": "Clanging Scales", + "FR": "Vibrécaille", + "DE": "Schuppenrasseln", + "IT": "Clamorsquame", + "RU": "Лязгающая Чешуя", + "ES": "Fragor Escamas" + }, + "pokemon_move_485": { + "PT-BR": "Aperto Esmagador", + "EN": "Crush Grip", + "FR": "Presse", + "DE": "Quetschgriff", + "IT": "Sbriciolmano", + "RU": "Сокрушительная Хватка", + "ES": "Agarrón" + }, + "pokemon_move_486": { + "PT-BR": "Energia Dracônica", + "EN": "Dragon Energy", + "FR": "Draco-Énergie", + "DE": "Drachenkräfte", + "IT": "Dragoenergia", + "RU": "Энергия Дракона", + "ES": "Dracoenergía" + }, + "pokemon_move_487": { + "PT-BR": "Passo Aquático", + "EN": "Aqua Step", + "FR": "Danse Aquatique", + "DE": "Wogentanz", + "IT": "Idroballetto", + "RU": "Танец Воды", + "ES": "Danza Acuática" + }, + "pokemon_move_488": { + "PT-BR": "Água Fria", + "EN": "Chilling Water", + "FR": "Douche Froide", + "DE": "Kalte Dusche", + "IT": "Doccia Fredda", + "RU": "Холодный Душ", + "ES": "Agua Fría" + }, + "pokemon_move_489": { + "PT-BR": "Espada Secreta", + "EN": "Secret Sword", + "FR": "Lame Ointe", + "DE": "Mystoschwert", + "IT": "Spadamistica", + "RU": "Тайный Меч", + "ES": "Sable Místico" + }, + "pokemon_move_490": { + "PT-BR": "Bico Explosivo", + "EN": "Beak Blast", + "FR": "Bec-Canon", + "DE": "Schnabelkanone", + "IT": "Cannonbecco", + "RU": "Взрывоопасный Клюв", + "ES": "Pico Cañón" + }, + "pokemon_move_491": { + "PT-BR": "Explosão Mental", + "EN": "Mind Blown", + "FR": "Caboche-Kaboum", + "DE": "Knallkopf", + "IT": "Sbalorditesta", + "RU": "Взрыв Разума", + "ES": "Cabeza Sorpresa" + }, + "pokemon_move_492": { + "PT-BR": "Toque do Tambor", + "EN": "Drum Beating", + "FR": "Tambour Battant", + "DE": "Trommelschläge", + "IT": "Tamburattacco", + "RU": "Барабанный Бой", + "ES": "Batería Asalto" + }, + "pokemon_move_493": { + "PT-BR": "Bola Incendiária", + "EN": "Pyro Ball", + "FR": "Ballon Brûlant", + "DE": "Feuerball", + "IT": "Palla Infuocata", + "RU": "Огнешар", + "ES": "Balón Ígneo" } } \ No newline at end of file diff --git a/logic.php b/logic.php index 35d4c6f4..3665c3d3 100644 --- a/logic.php +++ b/logic.php @@ -4,61 +4,11 @@ // Any new logic functions should be included directly where they are needed, // NOT via this file! -include('logic/active_raid_duplication_check.php'); -include('logic/alarm.php'); -include('logic/check_time.php'); -include('logic/cp_keys.php'); -include('logic/curl_json_response.php'); -include('logic/delete_raid.php'); -include('logic/delete_trainerinfo.php'); -include('logic/disable_raid_level.php'); -include('logic/edit_pokedex_keys.php'); -include('logic/get_chat_title_username.php'); -include('logic/get_formatted_pokemon_cp.php'); -include('logic/get_gym_by_telegram_id.php'); -include('logic/get_gym_details.php'); -include('logic/get_gym.php'); -include('logic/get_local_pokemon_name.php'); -include('logic/get_overview.php'); -include('logic/get_pokemon_by_table_id.php'); -include('logic/get_pokemon_cp.php'); -include('logic/get_pokemon_form_name.php'); -include('logic/get_pokemon_id_by_name.php'); -include('logic/get_pokemon_info.php'); -include('logic/get_pokemon_weather.php'); -include('logic/get_pokemon_shiny_status.php'); -include('logic/get_raid.php'); -include('logic/get_raid_times.php'); -include('logic/get_remote_users_count.php'); -include('logic/get_user.php'); -include('logic/get_weather_icons.php'); -include('logic/group_code_keys.php'); -include('logic/insert_cleanup.php'); -include('logic/insert_overview.php'); -include('logic/insert_trainerinfo.php'); -include('logic/keys_event.php'); -include('logic/keys_trainerinfo.php'); -include('logic/keys_vote.php'); -include('logic/mapslink.php'); -include('logic/new_user.php'); -include('logic/pokemon_keys.php'); -include('logic/raid_access_check.php'); -include('logic/raid_edit_gym_keys.php'); -include('logic/raid_edit_gyms_first_letter_keys.php'); -include('logic/raid_edit_raidlevel_keys.php'); -include('logic/raid_get_gyms_list_keys.php'); -include('logic/raid_level.php'); -include('logic/raid_list.php'); -include('logic/raid_poll_message.php'); -include('logic/sendalarmnotice.php'); -include('logic/send_trainerinfo.php'); -include('logic/send_vote_remote_users_limit_reached.php'); -include('logic/send_vote_time_first.php'); -include('logic/send_vote_time_future.php'); -include('logic/show_raid_poll.php'); -include('logic/show_raid_poll_small.php'); -include('logic/show_trainerinfo.php'); -include('logic/user_tutorial.php'); -include('logic/weather_keys.php'); -include('logic/curl_get_contents.php'); -?> +require_once('logic/date_util.php'); +require_once('logic/geo_api.php'); +require_once('logic/key_util.php'); +require_once('logic/language.php'); +require_once('logic/collectCleanup.php'); +require_once('logic/get_local_pokemon_name.php'); +require_once('logic/get_raid.php'); +require_once('logic/new_user.php'); diff --git a/logic/active_raid_duplication_check.php b/logic/active_raid_duplication_check.php index 9f6ee6dc..a9993583 100644 --- a/logic/active_raid_duplication_check.php +++ b/logic/active_raid_duplication_check.php @@ -1,35 +1,44 @@ (UTC_TIMESTAMP() - INTERVAL 5 MINUTE) - AND gym_id = {$gym_id} - ORDER BY event IS NOT NULL - " - ); - $active = 0; - while($raid = $rs->fetch()) { - if($config->RAID_EXCLUDE_EXRAID_DUPLICATION && $raid['event'] == EVENT_ID_EX) { - continue; - } - if($config->RAID_EXCLUDE_EVENT_DUPLICATION && $raid['event'] !== NULL && $raid['event'] != EVENT_ID_EX) { - continue; - } - $active = $raid['id']; - break; + $levelSql = ''; + $args = [$gym_id]; + if($level !== false) { + $levelSql = 'AND level = ?'; + $args[] = $level; + } + // Build query. + $rs = my_query(' + SELECT id, event, level, pokemon, pokemon_form, spawn + FROM raids + WHERE end_time > UTC_TIMESTAMP() + AND gym_id = ? + ' . $levelSql . ' + ORDER BY end_time, event IS NOT NULL + ', $args + ); + while($raid = $rs->fetch()) { + // In some cases (ex-raids, event raids and elite raids) gyms can have multiple raids saved to them. + // We ignore these raids when performing the duplication check. + if( ($config->RAID_EXCLUDE_EXRAID_DUPLICATION && $raid['event'] == EVENT_ID_EX) + or ($level != 9 && $config->RAID_EXCLUDE_ELITE_DUPLICATION && $raid['level'] == 9) + or ($config->RAID_EXCLUDE_EVENT_DUPLICATION && $raid['event'] !== NULL && $raid['event'] != EVENT_ID_EX)) { + debug_log("Ignoring any duplication at {$gym_id} due to event/ex raid: {$raid['id']}"); + continue; } - return $active; + debug_log("Duplicate raid found at {$gym_id}: {$raid['id']}"); + if($returnArray === true) return $raid; + else return $raid['id']; + } + return 0; } - -?> diff --git a/logic/alarm.php b/logic/alarm.php index c13f1641..ca242ed8 100644 --- a/logic/alarm.php +++ b/logic/alarm.php @@ -1,4 +1,6 @@ fetch(); - // Get Trainername - $answer_quests = check_trainername($answer_quests); - $username = '' . $answer_quests['name'] . ''; - // Get Trainercode - $trainercode = $answer_quests['trainercode']; - } + // Get user info if it's needed for the alarm + if(!empty($user_id)) { + // Name of the user, which executes a status update + $requestUserinfo = my_query('SELECT * FROM users WHERE user_id = ? LIMIT 1', [$user_id]); + $answer_quests = $requestUserinfo->fetch(); + // Get Trainername + $answer_quests = check_trainername($answer_quests); + $username = '' . $answer_quests['name'] . ''; + // Get Trainercode + $trainercode = $answer_quests['trainercode']; + }else { + // Set this to 0 so we get every attendee from database + $user_id = 0; + } - // Gym name and raid times - if(is_array($raid_id_array)) { - $raid = $raid_id_array; - }else { - $raid = get_raid($raid_id_array); - } - $raid_id = $raid['id']; + // Gym name and raid times + $raid = (is_array($raid_id_array) ? $raid_id_array : get_raid($raid_id_array)); - $gymname = $raid['gym_name']; - $raidtimes = str_replace(CR, '', str_replace(' ', '', get_raid_times($raid, false, true))); + $raid_id = $raid['id']; - // Get attend time. - if(!in_array($action, ['new_att','new_boss','change_time','group_code_private','group_code_public'])) { - $r = my_query("SELECT DISTINCT attend_time FROM attendance WHERE raid_id = {$raid_id} and user_id = {$user_id}"); - $a = $r->fetch(); - if(isset($a['attend_time'])) { - $attendtime = $a['attend_time']; - }else { - $attendtime = 0; - } - } + $gymname = $raid['gym_name']; - if($action == 'group_code_public' or $action == 'group_code_private') { - $request = my_query(" SELECT DISTINCT attendance.user_id, attendance.remote, users.lang - FROM attendance - LEFT JOIN users - ON users.id = attendance.user_id - WHERE raid_id = {$raid_id} - AND attend_time = (SELECT attend_time from attendance WHERE raid_id = {$raid_id} AND user_id = {$user_id}) - "); - }else { - $request = my_query(" SELECT DISTINCT attendance.user_id, users.lang - FROM attendance - LEFT JOIN users - ON users.id = attendance.user_id - WHERE raid_id = {$raid_id} - AND attendance.user_id != {$user_id} - AND cancel = 0 - AND raid_done = 0 - AND alarm = 1 - "); - } + // Get attend time. + if(!in_array($action, ['new_att','new_boss','change_time','group_code_private','group_code_public'])) { + $r = my_query('SELECT DISTINCT attend_time FROM attendance WHERE raid_id = ? and user_id = ? LIMIT 1', [$raid_id, $user_id]); + $a = $r->fetch(); - while($answer = $request->fetch()) - { - if(!isset($answer['lang']) or empty($answer['lang'])) $recipient_language = $config->LANGUAGE_PUBLIC; - else $recipient_language = $answer['lang']; - // Adding a guest - if($action == "extra") { - debug_log('Alarm additional trainer: ' . $info); - $icons = ['alien' => EMOJI_ALIEN, 'in_person' => EMOJI_IN_PERSON]; + $attendtime = isset($a['attend_time']) ? $a['attend_time'] : 0; + } - // Sending message - if($info == 'alien') { - $msg_text = '' . getTranslation('alert_add_alien_trainer', true, $recipient_language) . '' . CR; - }else { - $msg_text = '' . getTranslation('alert_add_trainer', true, $recipient_language) . '' . CR; - } - $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; - $msg_text .= EMOJI_SINGLE . SP . $username . SP . '+' . $icons[$info] . CR; - $msg_text .= EMOJI_CLOCK . SP . check_time($attendtime); - $msg_text .= create_traincode_msg($trainercode); + if($action == 'group_code_public' or $action == 'group_code_private') { + $request = my_query(' + SELECT DISTINCT attendance.user_id, attendance.remote, users.lang + FROM attendance + LEFT JOIN users + ON users.user_id = attendance.user_id + WHERE raid_id = :raidId + AND attend_time = (SELECT attend_time from attendance WHERE raid_id = :raidId AND user_id = :userId) + ', ['raidId' => $raid_id, 'userId' => $user_id]); + }else { + $request = my_query(' + SELECT DISTINCT attendance.user_id, users.lang + FROM attendance + LEFT JOIN users + ON users.user_id = attendance.user_id + WHERE raid_id = :raidId + AND attendance.user_id != :userId + AND cancel = 0 + AND raid_done = 0 + AND alarm = 1 + ', ['raidId' => $raid_id, 'userId' => $user_id]); + } - // Updating status - here or cancel - } else if($action == "status") { - // If trainer changes state (to late or cancelation) - if($info == 'late') { - debug_log('Alarm late: ' . $info); - // Send message. - $msg_text = '' . getTranslation('alert_later', true, $recipient_language) . '' . CR; - $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; - $msg_text .= EMOJI_SINGLE . SP . $username . CR; - $msg_text .= EMOJI_CLOCK . SP . check_time($attendtime); - $msg_text .= create_traincode_msg($trainercode); - } else if($info == 'cancel') { - debug_log('Alarm cancel: ' . $info); - $msg_text = '' . getTranslation('alert_cancel', true, $recipient_language) . '' . CR; - $msg_text .= TEAM_CANCEL . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; - $msg_text .= EMOJI_SINGLE . SP . $username . CR; - $msg_text .= EMOJI_CLOCK . SP . check_time($attendtime); - } + while($answer = $request->fetch()) + { + if(!isset($answer['lang']) or empty($answer['lang'])) $recipient_language = $config->LANGUAGE_PUBLIC; + else $recipient_language = $GLOBALS['languages'][$answer['lang']]; + $raidtimes = str_replace(CR, '', str_replace(' ', '', get_raid_times($raid, $recipient_language, true))); + // Adding a guest + if($action == 'extra') { + debug_log('Alarm additional trainer: ' . $info); + $icons = ['alien' => EMOJI_ALIEN, 'in_person' => EMOJI_IN_PERSON]; - // Updating pokemon - } else if($action == "pok_individual") { - debug_log('Alarm Pokemon: ' . $info); + // Sending message + if($info == 'alien') { + $msg_text = '' . getTranslation('alert_add_alien_trainer', $recipient_language) . '' . CR; + }else { + $msg_text = '' . getTranslation('alert_add_trainer', $recipient_language) . '' . CR; + } + $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; + $msg_text .= EMOJI_SINGLE . SP . $username . SP . '+' . $icons[$info] . CR; + $msg_text .= EMOJI_CLOCK . SP . check_time($attendtime, $recipient_language); + $msg_text .= create_traincode_msg($trainercode); - // Only a specific pokemon - if($info != '0') { - $pokemon = explode("-",$info,2); - $poke_name = get_local_pokemon_name($pokemon[0],$pokemon[1]); - $msg_text = '' . getTranslation('alert_individual_poke', true, $recipient_language) . SP . $poke_name . '' . CR; - $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; - $msg_text .= EMOJI_SINGLE . SP . $username . CR; - $msg_text .= EMOJI_CLOCK . SP . check_time($attendtime); - $msg_text .= create_traincode_msg($trainercode); - // Any pokemon - } else { - $msg_text = '' . getTranslation('alert_every_poke', true, $recipient_language) . '' . CR; - $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; - $msg_text .= EMOJI_SINGLE . SP . $username . CR; - $msg_text .= EMOJI_CLOCK . SP . check_time($attendtime); - $msg_text .= create_traincode_msg($trainercode); - } + // Updating status - here or cancel + } else if($action == 'status') { + // If trainer changes state (to late or cancelation) + if($info == 'late') { + debug_log('Alarm late: ' . $info); + // Send message. + $msg_text = '' . getTranslation('alert_later', $recipient_language) . '' . CR; + $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; + $msg_text .= EMOJI_SINGLE . SP . $username . CR; + $msg_text .= EMOJI_CLOCK . SP . check_time($attendtime, $recipient_language); + $msg_text .= create_traincode_msg($trainercode); + } else if($info == 'cancel') { + debug_log('Alarm cancel: ' . $info); + $msg_text = '' . getTranslation('alert_cancel', $recipient_language) . '' . CR; + $msg_text .= TEAM_CANCEL . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; + $msg_text .= EMOJI_SINGLE . SP . $username . CR; + $msg_text .= EMOJI_CLOCK . SP . check_time($attendtime, $recipient_language); + } - // Cancel pokemon - } else if($action == "pok_cancel_individual") { - debug_log('Alarm Pokemon: ' . $info); - $pokemon = explode("-",$info,2); - $poke_name = get_local_pokemon_name($pokemon[0],$pokemon[1]); - $msg_text = '' . getTranslation('alert_cancel_individual_poke', true, $recipient_language) . SP . $poke_name . '' . CR; - $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; - $msg_text .= EMOJI_SINGLE . SP . $username . CR; - $msg_text .= EMOJI_CLOCK . SP . check_time($attendtime); - $msg_text .= create_traincode_msg($trainercode); + // Updating pokemon + } else if($action == 'pok_individual') { + debug_log('Alarm Pokemon: ' . $info); - } else if($action == "new_boss") { - $msg_text = '' . getTranslation('alert_raid_boss') . '' . CR; - $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; - $msg_text .= EMOJI_EGG . SP . '' . get_local_pokemon_name($raid['pokemon'], $raid['pokemon_form']) . '' . CR; + if($info != '0') { + // Only a specific pokemon + $pokemon = explode("-",$info,2); + $poke_name = get_local_pokemon_name($pokemon[0], $pokemon[1], $recipient_language); + $msg_text = '' . getTranslation('alert_individual_poke', $recipient_language) . SP . $poke_name . '' . CR; + } else { + // Any pokemon + $msg_text = '' . getTranslation('alert_every_poke', $recipient_language) . '' . CR; + } + $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; + $msg_text .= EMOJI_SINGLE . SP . $username . CR; + $msg_text .= EMOJI_CLOCK . SP . check_time($attendtime, $recipient_language); + $msg_text .= create_traincode_msg($trainercode); - // New attendance - } else if($action == "new_att") { - debug_log('Alarm new attendance: ' . $info); - // Will Attend - $msg_text = '' . getTranslation('alert_new_att', true, $recipient_language) . '' . CR; - $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; - $msg_text .= EMOJI_SINGLE . SP . $username . CR; - $msg_text .= EMOJI_CLOCK . SP . check_time($info); - $msg_text .= create_traincode_msg($trainercode); + // Cancel pokemon + } else if($action == 'pok_cancel_individual') { + debug_log('Alarm Pokemon: ' . $info); + $pokemon = explode("-",$info,2); + $poke_name = get_local_pokemon_name($pokemon[0], $pokemon[1], $recipient_language); + $msg_text = '' . getTranslation('alert_cancel_individual_poke', $recipient_language) . SP . $poke_name . '' . CR; + $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; + $msg_text .= EMOJI_SINGLE . SP . $username . CR; + $msg_text .= EMOJI_CLOCK . SP . check_time($attendtime, $recipient_language); + $msg_text .= create_traincode_msg($trainercode); - // Attendance time change - } else if($action == "change_time") { - debug_log('Alarm changed attendance time: ' . $info); - // Changes Time - $msg_text = '' . getTranslation('alert_change_time', true, $recipient_language) . '' . CR; - $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; - $msg_text .= EMOJI_SINGLE . SP . $username . CR; - $msg_text .= EMOJI_CLOCK . SP . '' . check_time($info) . ''; - $msg_text .= create_traincode_msg($trainercode); + } else if($action == 'new_boss') { + $msg_text = '' . getTranslation('alert_raid_boss', $recipient_language) . '' . CR; + $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; + $msg_text .= EMOJI_EGG . SP . '' . get_local_pokemon_name($raid['pokemon'], $raid['pokemon_form'], $recipient_language) . '' . CR; - // Attendance from remote - } else if($action == "remote") { - debug_log('Alarm remote attendance changed: ' . $info); - // Changes Time - $msg_text = '' . getTranslation('alert_remote', true, $recipient_language) . '' . CR; - $msg_text .= EMOJI_REMOTE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; - $msg_text .= EMOJI_SINGLE . SP . $username . CR; - $msg_text .= EMOJI_CLOCK . SP . '' . check_time($attendtime) . ''; - $msg_text .= create_traincode_msg($trainercode); + // New attendance + } else if($action == 'new_att') { + debug_log('Alarm new attendance: ' . $info); + // Will Attend + $msg_text = '' . getTranslation('alert_new_att', $recipient_language) . '' . CR; + $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; + $msg_text .= EMOJI_SINGLE . SP . $username . CR; + $msg_text .= EMOJI_CLOCK . SP . check_time($info, $recipient_language); + $msg_text .= create_traincode_msg($trainercode); - // Attendance no longer from remote - } else if($action == "no_remote") { - debug_log('Alarm remote attendance changed: ' . $info); - // Changes Time - $msg_text = '' . getTranslation('alert_no_remote', true, $recipient_language) . '' . CR; - $msg_text .= EMOJI_REMOTE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; - $msg_text .= EMOJI_SINGLE . SP . $username . CR; - $msg_text .= EMOJI_CLOCK . SP . '' . check_time($attendtime) . ''; + // Attendance time change + } else if($action == 'change_time') { + debug_log('Alarm changed attendance time: ' . $info); + // Changes Time + $msg_text = '' . getTranslation('alert_change_time', $recipient_language) . '' . CR; + $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; + $msg_text .= EMOJI_SINGLE . SP . $username . CR; + $msg_text .= EMOJI_CLOCK . SP . '' . check_time($info, $recipient_language) . ''; + $msg_text .= create_traincode_msg($trainercode); - // No additional trainer - } else if($action == "extra_alone") { - debug_log('Alarm no additional trainers: ' . $info); - $msg_text = '' . getTranslation('alert_extra_alone', true, $recipient_language) . '' . CR; - $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; - $msg_text .= EMOJI_SINGLE . SP . $username . CR; - $msg_text .= EMOJI_CLOCK . SP . check_time($attendtime); - $msg_text .= create_traincode_msg($trainercode); + // Attendance from remote + } else if($action == 'remote') { + debug_log('Alarm remote attendance changed: ' . $info); + // Changes Time + $msg_text = '' . getTranslation('alert_remote', $recipient_language) . '' . CR; + $msg_text .= EMOJI_REMOTE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; + $msg_text .= EMOJI_SINGLE . SP . $username . CR; + $msg_text .= EMOJI_CLOCK . SP . '' . check_time($attendtime, $recipient_language) . ''; + $msg_text .= create_traincode_msg($trainercode); - // Group code public - } else if($action == "group_code_public") { - debug_log('Alarm for group code: ' . $info); - $msg_text = '' . getTranslation('alert_raid_starts_now', true, $recipient_language) . CR . getTranslation('alert_raid_get_in', true, $recipient_language) . '' . CR . CR; - $msg_text .= '' . getTranslation('alert_public_group', true, $recipient_language) . '' . CR; - $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; - $msg_text .= EMOJI_SINGLE . SP . $username . CR; - $msg_text .= EMOJI_REMOTE . SP . $info; + // Attendance no longer from remote + } else if($action == 'no_remote') { + debug_log('Alarm remote attendance changed: ' . $info); + // Changes Time + $msg_text = '' . getTranslation('alert_no_remote', $recipient_language) . '' . CR; + $msg_text .= EMOJI_REMOTE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; + $msg_text .= EMOJI_SINGLE . SP . $username . CR; + $msg_text .= EMOJI_CLOCK . SP . '' . check_time($attendtime, $recipient_language) . ''; - // Group code private - } else if($action == "group_code_private") { - debug_log('Alarm for group code: ' . $info); - $msg_text = '' . getTranslation('alert_raid_starts_now', true, $recipient_language) . CR . getTranslation('alert_raid_get_in', true, $recipient_language) . '' . CR . CR; - $msg_text .= '' . getTranslation('alert_private_group', true, $recipient_language) . '' . CR; - $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; - $msg_text .= EMOJI_SINGLE . SP . $username . CR; + // No additional trainer + } else if($action == 'extra_alone') { + debug_log('Alarm no additional trainers: ' . $info); + $msg_text = '' . getTranslation('alert_extra_alone', $recipient_language) . '' . CR; + $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; + $msg_text .= EMOJI_SINGLE . SP . $username . CR; + $msg_text .= EMOJI_CLOCK . SP . check_time($attendtime, $recipient_language); + $msg_text .= create_traincode_msg($trainercode); - // Send code to remote raiders - if($answer['remote'] == 1) { - $msg_text .= EMOJI_REMOTE . SP . '' . $info . ''; - } + // Group code public + } else if($action == 'group_code_public') { + debug_log('Alarm for group code: ' . $info); + $msg_text = '' . getTranslation('alert_raid_starts_now', $recipient_language) . CR . getTranslation('alert_raid_get_in', $recipient_language) . '' . CR . CR; + $msg_text .= '' . getTranslation('alert_public_group', $recipient_language) . '' . CR; + $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; + $msg_text .= EMOJI_SINGLE . SP . $username . CR; + $msg_text .= EMOJI_REMOTE . SP . $info; - // Send message to local raiders - if($answer['remote'] == 0) { - $msg_text .= EMOJI_REMOTE . SP . '' . getTranslation('group_code_only_for_remote_raiders', true, $recipient_language) . ''; - } - // Attendance from remote - } else if($action == "want_invite") { - debug_log('Alarm invite begging changed: ' . $info); - $msg_text = '' . getTranslation('alert_want_invite', true, $recipient_language) . '' . CR; - $msg_text .= EMOJI_WANT_INVITE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; - $msg_text .= EMOJI_SINGLE . SP . $username . CR; - $msg_text .= EMOJI_CLOCK . SP . '' . check_time($attendtime) . ''; - $msg_text .= create_traincode_msg($trainercode); + // Group code private + } else if($action == 'group_code_private') { + debug_log('Alarm for group code: ' . $info); + $msg_text = '' . getTranslation('alert_raid_starts_now', $recipient_language) . CR . getTranslation('alert_raid_get_in', $recipient_language) . '' . CR . CR; + $msg_text .= '' . getTranslation('alert_private_group', $recipient_language) . '' . CR; + $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; + $msg_text .= EMOJI_SINGLE . SP . $username . CR; - // Attendance no longer from remote - } else if($action == "no_want_invite") { - debug_log('Alarm invite begging changed: ' . $info); - $msg_text = '' . getTranslation('alert_no_want_invite', true, $recipient_language) . '' . CR; - $msg_text .= EMOJI_WANT_INVITE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; - $msg_text .= EMOJI_SINGLE . SP . $username . CR; - $msg_text .= EMOJI_CLOCK . SP . '' . check_time($attendtime) . ''; - $msg_text .= create_traincode_msg($trainercode); + // Send code to remote raiders + if($answer['remote'] == 1) { + $msg_text .= EMOJI_REMOTE . SP . '' . $info . ''; + } - // Let others know you are not playing, but can invite others - } else if($action == "can_invite") { - debug_log('Alarm: ' . $action); - $msg_text = '' . getTranslation('alert_can_invite', true, $recipient_language) . '' . CR; - $msg_text .= EMOJI_CAN_INVITE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; - $msg_text .= EMOJI_SINGLE . SP . $username . CR; - $msg_text .= EMOJI_CLOCK . SP . '' . check_time($attendtime) . ''; - $msg_text .= create_traincode_msg($trainercode); + // Send message to local raiders + if($answer['remote'] == 0) { + $msg_text .= EMOJI_REMOTE . SP . '' . getTranslation('group_code_only_for_remote_raiders', $recipient_language) . ''; + } + // Attendance from remote + } else if($action == 'want_invite') { + debug_log('Alarm invite begging changed: ' . $info); + $msg_text = '' . getTranslation('alert_want_invite', $recipient_language) . '' . CR; + $msg_text .= EMOJI_WANT_INVITE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; + $msg_text .= EMOJI_SINGLE . SP . $username . CR; + $msg_text .= EMOJI_CLOCK . SP . '' . check_time($attendtime, $recipient_language) . ''; + $msg_text .= create_traincode_msg($trainercode); - // Let others know you are not longer able to invite them - } else if($action == "no_can_invite") { - debug_log('Alarm: ' . $action); - $msg_text = '' . getTranslation('alert_no_can_invite', true, $recipient_language) . '' . CR; - $msg_text .= EMOJI_CAN_INVITE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; - $msg_text .= EMOJI_SINGLE . SP . $username . CR; - $msg_text .= EMOJI_CLOCK . SP . '' . check_time($attendtime) . ''; - $msg_text .= create_traincode_msg($trainercode); - } - $tg_json[] = send_message($answer['user_id'], $msg_text, false, false, true); + // Attendance no longer from remote + } else if($action == 'no_want_invite') { + debug_log('Alarm invite begging changed: ' . $info); + $msg_text = '' . getTranslation('alert_no_want_invite', $recipient_language) . '' . CR; + $msg_text .= EMOJI_WANT_INVITE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; + $msg_text .= EMOJI_SINGLE . SP . $username . CR; + $msg_text .= EMOJI_CLOCK . SP . '' . check_time($attendtime, $recipient_language) . ''; + $msg_text .= create_traincode_msg($trainercode); + + // Let others know you are not playing, but can invite others + } else if($action == 'can_invite') { + debug_log('Alarm: ' . $action); + $msg_text = '' . getTranslation('alert_can_invite', $recipient_language) . '' . CR; + $msg_text .= EMOJI_CAN_INVITE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; + $msg_text .= EMOJI_SINGLE . SP . $username . CR; + $msg_text .= EMOJI_CLOCK . SP . '' . check_time($attendtime, $recipient_language) . ''; + $msg_text .= create_traincode_msg($trainercode); + + // Let others know you are not longer able to invite them + } else if($action == 'no_can_invite') { + debug_log('Alarm: ' . $action); + $msg_text = '' . getTranslation('alert_no_can_invite', $recipient_language) . '' . CR; + $msg_text .= EMOJI_CAN_INVITE . SP . $gymname . SP . '(' . $raidtimes . ')' . CR; + $msg_text .= EMOJI_SINGLE . SP . $username . CR; + $msg_text .= EMOJI_CLOCK . SP . '' . check_time($attendtime, $recipient_language) . ''; + $msg_text .= create_traincode_msg($trainercode); } - return $tg_json; + $tg_json[] = send_message(create_chat_object([$answer['user_id']]), $msg_text, false, false, true); + } + return $tg_json; } /** @@ -271,9 +268,21 @@ function create_traincode_msg($trainercode){ global $config; $message = ''; if($config->RAID_POLL_SHOW_TRAINERCODE == true && !is_null($trainercode)) { - $message = CR . EMOJI_FRIEND . SP . '' . $trainercode . ''; + $message = CR . EMOJI_FRIEND . SP . '' . $trainercode . ''; } return $message; } -?> +/** + * Check attendance time against anytime. + * @param $time + * @param $recipientLanguage + */ +function check_time($time, $recipientLanguage) +{ + // Raid anytime? + if(strcmp($time, ANYTIME)===0){ + return getTranslation('anytime', $recipientLanguage); + } + return dt2time($time); +} diff --git a/logic/bearer_token.php b/logic/bearer_token.php new file mode 100644 index 00000000..77f6d085 --- /dev/null +++ b/logic/bearer_token.php @@ -0,0 +1,34 @@ + $version), botSpecificConfigFile('config.json')); +} + +/** + * Get current code revision from git state, or 'manual' if no git state exists. + * @return string + */ +function get_rev() +{ + if (!file_exists(ROOT_PATH . '/.git/HEAD')){ + debug_log('No .git/HEAD present, marking revision as manual'); + return 'manual'; + } + $ref = trim(file_get_contents(ROOT_PATH . '/.git/HEAD')); + if (ctype_xdigit($ref)){ + // Is already a hex string and thus valid rev + // Return first 6 digits of the hash + return substr($ref, 0, 6); + } + // strip 'ref: ' to get file path + $ref_file = ROOT_PATH . '/.git/' . substr($ref, 5); + if (file_exists($ref_file)){ + // Return first 6 digits of the hash, this matches our naming of docker image tags + return substr(file_get_contents($ref_file), 0, 6); + } + error_log("Git ref found but we cannot resolve it to a revision ({$ref_file}). Was the .git folder mangled?"); + return 'manual'; +} + +/** + * Bot upgrade check + * @param $current + * @param $latest + * @return + */ +function bot_upgrade_check($current, $latest) +{ + global $config, $metrics, $namespace, $dbh; + $orig = $current; // we may have to do multiple upgrades + if ($metrics){ + // This is the one place where we have full knowledge of version information & upgrades + $version_info = $metrics->registerGauge($namespace, 'version_info', 'Schema and revision information', ['current_schema', 'required_schema', 'rev', 'upgraded_timestamp', 'upgraded_from']); + } + + $upgrade_verdict = null; + $manual_upgrade_verdict = null; + // Same version? + if($current == $latest) { + return; + } + $upgrade_verdict = true; + if ($metrics && IS_INIT){ + // record initial version even if we don't do upgrades. + $version_info->set(1, [$current, $latest, get_rev(), null, null]); + } + // Check if upgrade files exist. + $upgrade_files = array(); + $upgrade_files = str_replace(UPGRADE_PATH . '/','', glob(UPGRADE_PATH . '/*.sql')); + if(!is_array($upgrade_files) or count($upgrade_files) == 0 or !in_array($latest . '.sql', $upgrade_files)) { + // No upgrade files found! Since the version now would only go up with upgrade files, something is off. + // It could be the user has bumped the VERSION file manually, or omitted upgrade files. + $error = 'NO SQL UPGRADE FILES FOUND FOR LATEST SCHEMA, THIS SHOULD NOT HAPPEN'; + throw new Exception($error); + } + // Check each sql filename. + foreach ($upgrade_files as $ufile) + { + $target = str_replace('.sql', '', $ufile); + // Skip every older sql file from array. + if($target <= $current) continue; + + if (!$config->UPGRADE_SQL_AUTO){ + $manual_upgrade_verdict = true; + debug_log("There's a schema upgrade to {$target} we could have run, but auto-upgrades have been disabled!"); + break; + } + info_log('PERFORMING AUTO SQL UPGRADE: ' . UPGRADE_PATH . '/' . $ufile, '!'); + require_once('sql_utils.php'); + if (!run_sql_file(UPGRADE_PATH . '/' . $ufile)) { + $manual_upgrade_verdict = true; + $error = 'AUTO UPGRADE FAILED: ' . UPGRADE_PATH . '/' . $ufile; + throw new Exception($error); + } + $manual_upgrade_verdict = false; + upgrade_config_version($target); + if ($metrics){ + $version_info->set(1, [$target, $latest, get_rev(), time(), $current]); + } + $current = $target; + } + // If previous sql upgrades had to be done and were successful, update also pokemon table + if($upgrade_verdict === true && $manual_upgrade_verdict === false) { + require_once(ROOT_PATH . '/mods/getdb.php'); + } + + // Signal whether manual action is required or not. + if ($manual_upgrade_verdict === true){ + $error = "The bot has pending schema upgrades ({$current} -> {$latest}) but you've disabled automatic upgrades. Nothing will work until you go do the upgrade(s) manually. You'll find them in the dir sql/upgrade/"; + throw new Exception($error); + } +} diff --git a/logic/check_time.php b/logic/check_time.php deleted file mode 100644 index f1e6af25..00000000 --- a/logic/check_time.php +++ /dev/null @@ -1,16 +0,0 @@ - diff --git a/logic/collectCleanup.php b/logic/collectCleanup.php new file mode 100644 index 00000000..b6a321a5 --- /dev/null +++ b/logic/collectCleanup.php @@ -0,0 +1,81 @@ + $save_id, + ':unique_id' => $unique_id, + ':pokedex_id' => $identifier['pokemon'], + ':form_id' => $identifier['pokemon_form'], + ':raid_id' => ($identifier['raid_ended'] ? 0 : $identifier['id']), // No need to save raid id if raid has ended + ':ended' => $identifier['raid_ended'], + ':start_time' => ($identifier['raid_ended'] ? NULL : $identifier['start_time']), + ':end_time' => ($identifier['raid_ended'] ? NULL : $identifier['end_time']), + ':gym_id' => $identifier['gym_id'], + ':standalone' => $standalone_photo, + ] + ); + } + $raid_id = is_array($identifier) ? $identifier['id'] : $identifier; + insert_cleanup($chat_id, $message_id, $thread_id, $raid_id, $type, $unique_id); + // Return response. + return $response; +} diff --git a/logic/config_chats.php b/logic/config_chats.php new file mode 100644 index 00000000..468a4894 --- /dev/null +++ b/logic/config_chats.php @@ -0,0 +1,87 @@ +CHATS_SHARE)) { + $chat_vars = ['TRAINER_CHATS','SHARE_CHATS','WEBHOOK_CHATS']; + $chatsTemp = []; + foreach(get_object_vars($config) as $var => $value) { + foreach($chat_vars as $start) { + if(!is_string($var) || strpos(trim($var), $start) === false) continue; + if($var == 'WEBHOOK_CHATS_BY_POKEMON') continue; + if(is_string($config->{$var})) { + array_merge($chatsTemp, explode(',', $config->{$var})); + continue; + }elseif(is_int($config->{$var})) { + $chatsTemp[] = $config->{$var}; + continue; + } + array_merge($chatsTemp, $config->{$var}); + } + } + foreach(array_unique($chatsTemp) as $chat) { + $chats[] = create_chat_object([$chat]); + } + }else { + $chats = []; + if(isset($config->CHATS_SHARE['manual_share'])) { + foreach($config->CHATS_SHARE['manual_share'] as $chatGroup) { + foreach($chatGroup as $chat) { + $chats = add_chat($chats, $chat); + } + } + } + if(isset($config->CHATS_SHARE['after_attendance'])) { + foreach($config->CHATS_SHARE['after_attendance'] as $chat) { + $chats = add_chat($chats, $chat); + } + } + if(isset($config->CHATS_SHARE['webhook'])) { + if(isset($config->CHATS_SHARE['webhook']['all'])) { + foreach($config->CHATS_SHARE['webhook']['all'] as $chat) { + $chats = add_chat($chats, $chat); + } + } + if(isset($config->CHATS_SHARE['webhook']['by_pokemon'])) { + foreach($config->CHATS_SHARE['webhook']['by_pokemon'] as $chatGroup) { + foreach($chatGroup['chats'] as $chat) { + $chats = add_chat($chats, $chat); + } + } + } + if(isset($config->CHATS_SHARE['webhook']['geofences'])) { + foreach($config->CHATS_SHARE['webhook']['geofences'] as $geofence) { + foreach($geofence as $chatGroup) { + foreach($chatGroup as $chat) { + $chats = add_chat($chats, $chat); + } + } + } + } + } + } + return $chats; +} + +function get_config_chat_by_chat_and_thread_id($chat_id, $thread_id) { + foreach(list_config_chats_by_short_id() as $chat) { + if($chat['id'] == $chat_id && ($thread_id == NULL && !isset($chat['thread']) || (isset($chat['thread']) && $chat['thread'] == $thread_id))) + return $chat; + } +} + +function add_chat($chats, $chatToAdd) { + foreach($chats as $chat) { + if( + $chat['id'] == $chatToAdd['id'] && !isset($chat['thread']) || + $chat['id'] == $chatToAdd['id'] && isset($chat['thread']) && isset($chatToAdd['thread']) && $chat['thread'] == $chatToAdd['thread'] + ) return $chats; + } + $chats[] = $chatToAdd; + return $chats; +} + +function get_config_chat_by_short_id($id) { + $chats = list_config_chats_by_short_id(); + return $chats[$id]; +} diff --git a/logic/cp_keys.php b/logic/cp_keys.php deleted file mode 100644 index 0d68cc1a..00000000 --- a/logic/cp_keys.php +++ /dev/null @@ -1,106 +0,0 @@ - $i, - 'callback_data' => $pokedex_id . ':' . $action . ':' . $new_cp - ); - } - - // 4 5 6 - for ($i = 4; $i <= 6; $i = $i + 1) { - // Set new cp - $new_cp = $cp_add . ($old_cp == 0 ? '' : $old_cp) . $i; - - // Set keys. - $keys[] = array( - 'text' => $i, - 'callback_data' => $pokedex_id . ':' . $action . ':' . $new_cp - ); - } - - // 1 2 3 - for ($i = 1; $i <= 3; $i = $i + 1) { - // Set new cp - $new_cp = $cp_add . ($old_cp == 0 ? '' : $old_cp) . $i; - - // Set keys. - $keys[] = array( - 'text' => $i, - 'callback_data' => $pokedex_id . ':' . $action . ':' . $new_cp - ); - } - - // 0 - if($old_cp != 0) { - // Set new cp - $new_cp = $cp_add . $old_cp . '0'; - } else { - $new_cp = $reset_arg; - } - - // Set keys. - $keys[] = array( - 'text' => '0', - 'callback_data' => $pokedex_id . ':' . $action . ':' . $new_cp - ); - } - - // Save - $keys[] = array( - 'text' => EMOJI_DISK, - 'callback_data' => $pokedex_id . ':' . $action . ':' . $save_arg - ); - - // Reset - $keys[] = array( - 'text' => getTranslation('reset'), - 'callback_data' => $pokedex_id . ':' . $action . ':' . $reset_arg - ); - - // Get the inline key array. - $keys = inline_key_array($keys, 3); - - return $keys; -} - -?> diff --git a/logic/createRaidBossList.php b/logic/createRaidBossList.php new file mode 100644 index 00000000..c5f4a9c9 --- /dev/null +++ b/logic/createRaidBossList.php @@ -0,0 +1,82 @@ +RAID_BOSS_LIST_RAID_LEVELS). ')'; + $q = my_query(' + SELECT + pokedex_id, pokemon_form_id, date_start, date_end, raid_level, + CONCAT(DATE_FORMAT(date_start,"%d%m%y%k"), DATE_FORMAT(date_end,"%d%m%y%k")) AS arrkey, + CASE WHEN date(date_start) = date(date_end) THEN 1 ELSE 0 END AS sameDay + FROM raid_bosses + WHERE raid_level IN ' . $levelList . ' + AND date_end > DATE_SUB(NOW(), INTERVAL 1 HOUR) + AND disabled = 0 + ORDER BY sameDay, date_start, date_end + '); + $list = ''; + $prevStartDate = ''; + $data[0] = $data[1] = []; + // Save the results in easy to process format + foreach($q->fetchAll() as $row) { + $data[$row['sameDay']][$row['arrkey']][] = $row; + } + if($q->rowCount() == 0) return ''; + $i = 1; + $list = $config->RAID_BOSS_LIST_TITLE; + // Print list of bosses that run for multiple days + foreach($data[0] as $tempRow) { + $list .= PHP_EOL . '- '; + foreach($tempRow as $num => $row) { + $pokemonName = get_local_pokemon_name($row['pokedex_id'], $row['pokemon_form_id'], $config->LANGUAGE_PUBLIC); + if(in_array($row['raid_level'], RAID_LEVEL_SHADOW)) $pokemonName .= ' ' . getPublicTranslation('shadow'); + if($num != 0) $list .= ', '; + $list .= $pokemonName; + } + $dateStart = new dateTime($row['date_start']); + $dateEnd = new dateTime($row['date_end']); + $list .= ' ' . $dateStart->format($dateFormat) . ' - '. $dateEnd->format($dateFormat); + $i++; + if($i > $config->RAID_BOSS_LIST_ROW_LIMIT) break; + } + + // Print list of one day bosses + foreach($data[1] as $arrkey => $tempRow) { + $startDate = substr($arrkey, 0, 6); + if($list != '' && $prevStartDate != $startDate) $list.= PHP_EOL . PHP_EOL; + foreach($tempRow as $num => $row) { + $dateStart = new dateTime($row['date_start']); + $dateEnd = new dateTime($row['date_end']); + if($num == 0){ + if($prevStartDate != $startDate) { + $list .= $dateStart->format($dateFormat); + } + $list .= PHP_EOL . '- '; + } + $pokemonName = get_local_pokemon_name($row['pokedex_id'], $row['pokemon_form_id'], $config->LANGUAGE_PUBLIC); + if($num != 0) $list .= ', '; + $list .= $pokemonName; + $prevStartDate = $startDate; + } + $list .= ' ' . $dateStart->format($timeFormat) . ' - ' . $dateEnd->format($timeFormat); + } + return $list; +} +?> \ No newline at end of file diff --git a/logic/curl_get_contents.php b/logic/curl_get_contents.php index 1f32f4db..2f829770 100644 --- a/logic/curl_get_contents.php +++ b/logic/curl_get_contents.php @@ -1,22 +1,20 @@ diff --git a/logic/curl_json_response.php b/logic/curl_json_response.php deleted file mode 100644 index 7c0855e8..00000000 --- a/logic/curl_json_response.php +++ /dev/null @@ -1,56 +0,0 @@ - {$json_response}", 'ERROR:'); - } else { - if($identifier != false) { - if (isset($response['result']['chat']['type']) && in_array($response['result']['chat']['type'], ['channel','group','supergroup'])) { - // Set chat and message_id - $chat_id = $response['result']['chat']['id']; - $message_id = $response['result']['message_id']; - debug_log('Return data: Chat id: '.$chat_id.', message_id: '.$message_id.', type: '.$identifier); - if($identifier == 'trainer') { - debug_log('Adding trainermessage info to database now!'); - insert_trainerinfo($chat_id, $message_id); - }else if ($identifier == 'overview') { - debug_log('Adding overview info to database now!'); - $chat_title = $response['result']['chat']['title']; - $chat_username = isset($response['result']['chat']['username']) ? $response['result']['chat']['username'] : ''; - - insert_overview($chat_id, $message_id, $chat_title, $chat_username); - }else { - if(isset($response['result']['text']) && !empty($response['result']['text'])) { - $type = 'poll_text'; - } else if(isset($response['result']['caption']) && !empty($response['result']['caption'])) { - $type = 'poll_photo'; - } else if(isset($response['result']['venue']) && !empty($response['result']['venue'])) { - $type = 'poll_venue'; - }else if(isset($response['result']['photo']) && !isset($response['result']['caption'])) { - $type = 'photo'; - } - insert_cleanup($chat_id, $message_id, $identifier, $type); - } - } - } - } - - // Return response. - return $response; -} - -?> diff --git a/logic/date_util.php b/logic/date_util.php new file mode 100644 index 00000000..f8af7bb2 --- /dev/null +++ b/logic/date_util.php @@ -0,0 +1,101 @@ +format('Y-m-d'); +} + +/** + * Get current utc datetime. + * @param $format + * @return string + */ +function utcnow($format = 'Y-m-d H:i:s') +{ + // Create a object with UTC timezone + $datetime = new DateTime('now', new DateTimeZone('UTC')); + + return $datetime->format($format); +} + +/** + * Format utc time from datetime value. + * @param $datetime_value + * @param $format + * @return string + */ +function utctime($datetime_value, $format = 'H:i') +{ + // Create a object with UTC timezone + $datetime = new DateTime($datetime_value, new DateTimeZone('UTC')); + + return $datetime->format($format); +} + +/** + * Get date from datetime value. + * @param $datetime_value + * @param $tz + * @return string + */ +function dt2date($datetime_value, $tz = NULL) +{ + global $config; + if($tz == NULL){ + $tz = $config->TIMEZONE; + } + // Create a object with UTC timezone + $datetime = new DateTime($datetime_value, new DateTimeZone('UTC')); + + // Change the timezone of the object without changing it's time + $datetime->setTimezone(new DateTimeZone($tz)); + + return $datetime->format('Y-m-d'); +} + +/** + * Get time from datetime value. + * @param $datetime_value + * @param $format + * @param $tz + * @return string + */ +function dt2time($datetime_value, $format = 'H:i', $tz = NULL) +{ + global $config; + if($tz == NULL){ + $tz = $config->TIMEZONE; + } + // Create a object with UTC timezone + $datetime = new DateTime($datetime_value, new DateTimeZone('UTC')); + + // Change the timezone of the object without changing it's time + $datetime->setTimezone(new DateTimeZone($tz)); + + return $datetime->format($format); +} + +function tz_diff($src = 'UTC', $dst = NULL) { + global $config; + if($dst == NULL){ + $dst = $config->TIMEZONE; + } + $dateTimeZoneSrc = new DateTimeZone($src); + $dateTimeZoneDst = new DateTimeZone($dst); + $dateTimeSrc = new datetime('now',$dateTimeZoneSrc); + $diff = $dateTimeZoneDst->getOffset($dateTimeSrc); + + $hours = str_pad(floor($diff/60/60),2,'0',STR_PAD_LEFT); + $minutes = str_pad(floor(($diff-$hours*60*60)/60),2,'0',STR_PAD_LEFT); + if($diff < 0) { + return '-'.$hours.':'.$minutes; + } + return '+'.$hours.':'.$minutes; +} diff --git a/logic/debug.php b/logic/debug.php new file mode 100644 index 00000000..34098356 --- /dev/null +++ b/logic/debug.php @@ -0,0 +1,144 @@ +DEBUG_LOGFILE, + $config->LOGGING_INFO_LOGFILE, + $config->DEBUG_LOGFILE, + $config->DEBUG_INCOMING_LOGFILE, + $config->DEBUG_SQL_LOGFILE, + $config->CLEANUP_LOGFILE, + ]; + + # Collect unique paths that house logfiles + $paths = []; + foreach($logfiles as $logfile){ + $dirname = pathinfo($logfile, PATHINFO_DIRNAME); + if(!in_array($dirname, $paths)){ + $paths[] = $dirname; + } + } + + # Create the necessary paths + foreach($paths as $path){ + if (!file_exists($path)) { + mkdir($path, 770, true); + } + } +} +/** + * Write any log level. + * @param $val + * @param string $type + */ +function generic_log($val, $type, $logfile) +{ + $date = @date('Y-m-d H:i:s'); + $usec = microtime(true); + $date = $date . '.' . str_pad(substr($usec, 11, 4), 4, '0', STR_PAD_RIGHT); + + $bt = debug_backtrace(); + $bl = ''; + + // How many calls back to print + // Increasing this makes it easier to hunt down issues, but increases log line length + $layers = 1; + + while ($btl = array_shift($bt)) { + // Ignore generic_log and it's calling function in the call stack + // Not sure why it works exactly like that, but it does. + if ($btl['function'] == __FUNCTION__){ + continue; + } + --$layers; + $bl = $bl . '[' . basename($btl['file']) . ':' . $btl['line'] . ']'; + if($layers <= 0) { + $bl = $bl . ' '; + break; + } + } + + if (gettype($val) != 'string') $val = var_export($val, 1); + $rows = explode("\n", $val); + foreach ($rows as $v) { + error_log('[' . $date . '][' . getmypid() . '] ' . $bl . $type . ' ' . $v . "\n", 3, $logfile); + } +} + +/** + * Write debug log. + * @param $val + * @param string $type + */ +function debug_log($message, $type = '*') +{ + global $config; + // Write to log only if debug is enabled. + if ($config->DEBUG === false){ + return; + } + generic_log($message, $type, $config->DEBUG_LOGFILE); +} + +/** + * Write cleanup log. + * @param $message + * @param string $type + */ +function cleanup_log($message, $type = '*'){ + global $config; + // Write to log only if cleanup logging is enabled. + if ($config->CLEANUP_LOG === false){ + return; + } + generic_log($message, $type, $config->CLEANUP_LOGFILE); +} + +/** + * Write sql debug log. + * @param $message + * @param string $type + */ +function debug_log_sql($message, $type = '%'){ + global $config; + // Write to log only if debug is enabled. + if ($config->DEBUG_SQL === false){ + return; + } + generic_log($message, $type, $config->DEBUG_SQL_LOGFILE); +} + +/** + * Write incoming stream debug log. + * @param $message + * @param string $type + */ +function debug_log_incoming($message, $type = '<'){ + global $config; + // Write to log only if debug is enabled. + if ($config->DEBUG_INCOMING === false){ + return; + } + generic_log($message, $type, $config->DEBUG_INCOMING_LOGFILE); +} + +/** + * Write INFO level log. + * @param $message + * @param string $type + */ +function info_log($message, $type = '[I]'){ + global $config; + // Write to log only if info logging is enabled. + if ($config->LOGGING_INFO === false){ + return; + } + generic_log($message, $type, $config->LOGGING_INFO_LOGFILE); +} diff --git a/logic/delete_raid.php b/logic/delete_raid.php deleted file mode 100644 index ade9f0f3..00000000 --- a/logic/delete_raid.php +++ /dev/null @@ -1,63 +0,0 @@ - 0 - " - ); - - // Counter - $counter = 0; - - // Delete every telegram message - while ($row = $rs->fetch()) { - // Delete telegram message. - debug_log('Deleting telegram message ' . $row['message_id'] . ' from chat ' . $row['chat_id'] . ' for raid ' . $row['raid_id']); - delete_message($row['chat_id'], $row['message_id']); - $counter = $counter + 1; - } - - // Nothing to delete on telegram. - if ($counter == 0) { - debug_log('Raid with ID ' . $raid_id . ' was not found in the cleanup table! Skipping deletion of telegram messages!'); - } - - // Delete raid from cleanup table. - debug_log('Deleting raid ' . $raid_id . ' from the cleanup table:'); - $rs_cleanup = my_query( - " - DELETE FROM cleanup - WHERE raid_id = '{$raid_id}' - " - ); - - // Delete raid from attendance table. - debug_log('Deleting raid ' . $raid_id . ' from the attendance table:'); - $rs_attendance = my_query( - " - DELETE FROM attendance - WHERE raid_id = '{$raid_id}' - " - ); - - // Delete raid from raid table. - debug_log('Deleting raid ' . $raid_id . ' from the raid table:'); - $rs_raid = my_query( - " - DELETE FROM raids - WHERE id = '{$raid_id}' - " - ); -} - - -?> diff --git a/logic/delete_trainerinfo.php b/logic/delete_trainerinfo.php index 46d88d1c..587bcace 100644 --- a/logic/delete_trainerinfo.php +++ b/logic/delete_trainerinfo.php @@ -6,18 +6,15 @@ */ function delete_trainerinfo($chat_id, $message_id) { - // Delete telegram message. - debug_log('Deleting trainer info telegram message ' . $message_id . ' from chat ' . $chat_id); - delete_message($chat_id, $message_id); + // Delete telegram message. + debug_log('Deleting trainer info telegram message ' . $message_id . ' from chat ' . $chat_id); + delete_message($chat_id, $message_id); - // Delete trainer info from database. - debug_log('Deleting trainer information from database for Chat_ID: ' . $chat_id); - $rs = my_query( - " - DELETE FROM trainerinfo - WHERE chat_id = '{$chat_id}' - " - ); + // Delete trainer info from database. + debug_log('Deleting trainer information from database for Chat_ID: ' . $chat_id); + my_query(' + DELETE FROM trainerinfo + WHERE chat_id = ? + ', [$chat_id] + ); } - -?> diff --git a/logic/disable_raid_level.php b/logic/disable_raid_level.php index 55fd8fe3..977591a4 100644 --- a/logic/disable_raid_level.php +++ b/logic/disable_raid_level.php @@ -1,19 +1,15 @@ diff --git a/logic/download_Portal_Image.php b/logic/download_Portal_Image.php new file mode 100644 index 00000000..67e42b5e --- /dev/null +++ b/logic/download_Portal_Image.php @@ -0,0 +1,40 @@ + $gym_id, 'a' => 'show', 'v' => $arg_show]), + button($text_ex_button, ['gym_edit_details', 'g' => $gym_id, 'a' => 'ex', 'v' => $arg_ex]) + ]; + if($botUser->accessCheck('gym-name', true)) { + $keys[][] = button(EMOJI_PENCIL . ' ' . getTranslation('gym_name_edit'), ['gym_edit_details', 'g' => $gym_id, 'a' => 'name']); + } + if($botUser->accessCheck('gym-edit', true)) { + $keys[][] = button( + EMOJI_INFO . ' ' . (!empty($gym_note) ? getTranslation('edit') : getTranslation('add') ) . ' ' . getTranslation('gym_add_edit_note'), + ['gym_edit_details', 'g' => $gym_id, 'a' => 'note'] + ); + $keys[][] = button( + EMOJI_MAP . ' ' . ((!empty($gym_address) && $gym_address != getTranslation('directions')) ? getTranslation('edit') : getTranslation('add')) . ' ' . getTranslation('gym_address'), + ['gym_edit_details', 'g' => $gym_id, 'a' => 'addr'] + ); + $keys[][] = button( + EMOJI_HERE . ' ' . getTranslation('gym_edit_coordinates'), + ['gym_edit_details', 'g' => $gym_id, 'a' => 'gps'] + ); + } + if($botUser->accessCheck('gym-delete', true)) { + $keys[][] = button(EMOJI_DELETE . ' ' . getTranslation('gym_delete'), ['gym_delete', 'g' => $gym_id, 'c' => 0]); + } + $keys[][] = button(getTranslation('done'), ['exit', 'd' => '1']); + + return $keys; +} diff --git a/logic/edit_pokedex_keys.php b/logic/edit_pokedex_keys.php index 05a7ca4c..20471f3d 100644 --- a/logic/edit_pokedex_keys.php +++ b/logic/edit_pokedex_keys.php @@ -2,103 +2,73 @@ /** * Pokedex edit pokemon keys. * @param $limit - * @param $action * @return array */ -function edit_pokedex_keys($limit, $action) +function edit_pokedex_keys($limit) { - // Number of entries to display at once. - $entries = 10; - - // Number of entries to skip with skip-back and skip-next buttons - $skip = 50; - - // Module for back and next keys - $module = "pokedex"; - - // Init empty keys array. - $keys = []; - - // Get all pokemon from database - $rs = my_query( - " - SELECT pokedex_id, pokemon_form_id - FROM pokemon - ORDER BY pokedex_id, pokemon_form_name != 'normal', pokemon_form_name - LIMIT $limit, $entries - " - ); - - // Number of entries - $cnt = my_query( - " - SELECT COUNT(*) AS count - FROM pokemon - " - ); - - // Number of database entries found. - $sum = $cnt->fetch(); - $count = $sum['count']; - - // List users / moderators - while ($mon = $rs->fetch()) { - $pokemon_name = get_local_pokemon_name($mon['pokedex_id'], $mon['pokemon_form_id']); - $keys[] = array( - 'text' => $mon['pokedex_id'] . SP . $pokemon_name, - 'callback_data' => $mon['pokedex_id'] . '-' . $mon['pokemon_form_id'] . ':pokedex_edit_pokemon:0' - ); - } - - // Empty backs and next keys - $keys_back = []; - $keys_next = []; - - // Add back key. - if ($limit > 0) { - $new_limit = $limit - $entries; - $empty_back_key = []; - $back = universal_key($empty_back_key, $new_limit, $module, $action, getTranslation('back') . " (-" . $entries . ")"); - $keys_back[] = $back[0][0]; - } - - // Add skip back key. - if ($limit - $skip > 0) { - $new_limit = $limit - $skip - $entries; - $empty_back_key = []; - $back = universal_key($empty_back_key, $new_limit, $module, $action, getTranslation('back') . " (-" . $skip . ")"); - $keys_back[] = $back[0][0]; - } - - // Add next key. - if (($limit + $entries) < $count) { - $new_limit = $limit + $entries; - $empty_next_key = []; - $next = universal_key($empty_next_key, $new_limit, $module, $action, getTranslation('next') . " (+" . $entries . ")"); - $keys_next[] = $next[0][0]; - } - - // Add skip next key. - if (($limit + $skip + $entries) < $count) { - $new_limit = $limit + $skip + $entries; - $empty_next_key = []; - $next = universal_key($empty_next_key, $new_limit, $module, $action, getTranslation('next') . " (+" . $skip . ")"); - $keys_next[] = $next[0][0]; - } - - // Exit key - $empty_exit_key = []; - $key_exit = universal_key($empty_exit_key, "0", "exit", "0", getTranslation('abort')); - - // Get the inline key array. - $keys = inline_key_array($keys, 1); - $keys_back = inline_key_array($keys_back, 2); - $keys_next = inline_key_array($keys_next, 2); - $keys = array_merge($keys_back, $keys); - $keys = array_merge($keys, $keys_next); - $keys = array_merge($keys, $key_exit); - - return $keys; + // Number of entries to display at once. + $entries = 10; + + // Number of entries to skip with skip-back and skip-next buttons + $skip = 50; + + // Init empty keys array. + $pokemonKeys = []; + + // Get all pokemon from database + $rs = my_query(' + SELECT pokedex_id, pokemon_form_id + FROM pokemon + ORDER BY pokedex_id, pokemon_form_name != \'normal\', pokemon_form_name + LIMIT ' . $limit . ',' . $entries + ); + + // Number of entries + $cnt = my_query(' + SELECT COUNT(*) AS count + FROM pokemon + '); + + // Number of database entries found. + $sum = $cnt->fetch(); + $count = $sum['count']; + + // List users / moderators + while ($mon = $rs->fetch()) { + $pokemon_name = get_local_pokemon_name($mon['pokedex_id'], $mon['pokemon_form_id']); + $pokemonKeys[][] = button($mon['pokedex_id'] . SP . $pokemon_name, ['pokedex_edit_pokemon', 'p' => $mon['pokedex_id'] . '-' . $mon['pokemon_form_id']]); + } + + // Empty backs and next keys + $keys_back = $keys_next = []; + + // Add back key. + if ($limit > 0) { + $new_limit = $limit - $entries; + $keys_back[0][] = button(getTranslation('back') . ' (-' . $entries . ')',['pokedex', 'l' => $new_limit]); + } + + // Add skip back key. + if ($limit - $skip > 0) { + $new_limit = $limit - $skip - $entries; + $keys_back[0][] = button(getTranslation('back') . ' (-' . $skip . ')', ['pokedex', 'l' => $new_limit]); + } + + // Add next key. + if (($limit + $entries) < $count) { + $new_limit = $limit + $entries; + $keys_next[0][] = button(getTranslation('next') . ' (+' . $entries . ')', ['pokedex', 'l' => $new_limit]); + } + + // Add skip next key. + if (($limit + $skip + $entries) < $count) { + $new_limit = $limit + $skip + $entries; + $keys_next[0][] = button(getTranslation('next') . ' (+' . $skip . ')', ['pokedex', 'l' => $new_limit]); + } + + // Get the inline key array. + $keys = array_merge($keys_back, $pokemonKeys, $keys_next); + $keys[][] = button(getTranslation('abort'), 'exit'); + + return $keys; } - -?> diff --git a/logic/geo_api.php b/logic/geo_api.php new file mode 100644 index 00000000..370bbc0c --- /dev/null +++ b/logic/geo_api.php @@ -0,0 +1,240 @@ +MAPS_LOOKUP && !empty($config->MAPS_API_KEY)) { + // Init defaults. + $location = []; + $location['street'] = ''; + $location['street_number'] = ''; + $location['postal_code'] = ''; + $location['district'] = ''; + + // Set maps geocode url. + $language = strtolower($config->LANGUAGE_PUBLIC); + $MapsApiKey = $config->MAPS_API_KEY; + $url = 'https://maps.googleapis.com/maps/api/geocode/json?latlng=' . $lat . ',' . $lon . '&language=' . $language; + $url .= '&key=' . $MapsApiKey; + + // Curl request. + $curl = curl_init($url); + curl_setopt($curl, CURLOPT_HEADER, false); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + + // Proxy server? + // Use Proxyserver for curl if configured + if ($config->CURL_USEPROXY) { + curl_setopt($curl, CURLOPT_PROXY, $config->CURL_PROXYSERVER); + } + + // Curl response. + $json_response = curl_exec($curl); + + // Write request and response to log. + debug_log($url, 'G>'); + debug_log($json_response, 'status) || $data->status != 'OK' && empty($data->results)) { + // No valid data received. + return false; + } + + // Init vars. + $locality = ''; + $sublocalityLv2 = ''; + $sublocality = ''; + + // Iterate each result. + foreach ($data->results as $result) { + + // Check for address components. + if (!empty($result->address_components)) { + // Iterate each address component. + foreach ($result->address_components as $address_component) { + + // Street found. + if (in_array('route', $address_component->types) && !empty($address_component->long_name)) { + // Set street by first found. + $location['street'] = empty($location['street']) ? $address_component->long_name : $location['street']; + } + + // Street number found. + if (in_array('street_number', $address_component->types) && !empty($address_component->long_name)) { + // Set street by first found. + $location['street_number'] = empty($location['street_number']) ? $address_component->long_name : $location['street_number']; + } + + // Postal code found. + if (in_array('postal_code', $address_component->types) && !empty($address_component->long_name)) { + // Set street by first found. + $location['postal_code'] = empty($location['postal_code']) ? $address_component->long_name : $location['postal_code']; + } + + // Sublocality level2 found. + if (in_array('sublocality_level_2', $address_component->types) && !empty($address_component->long_name)) { + // Set sublocality level 2 by first found. + $sublocalityLv2 = empty($sublocalityLv2) ? $address_component->long_name : $sublocalityLv2; + } + + // Sublocality found. + if (in_array('sublocality', $address_component->types) && !empty($address_component->long_name)) { + // Set sublocality by first found. + $sublocality = empty($sublocality) ? $address_component->long_name : $sublocality; + } + + // Locality found. + if (in_array('locality', $address_component->types) && !empty($address_component->long_name)) { + // Set sublocality by first found. + $locality = empty($sublocality) ? $address_component->long_name : $sublocality; + } + } + } + break; + } + + // Set district by priority. + if (!empty($sublocalityLv2)) { + $location['district'] = $sublocalityLv2; + + } else if ($sublocality) { + $location['district'] = $sublocality; + + } else if ($locality) { + $location['district'] = $locality; + } + + // Rename street responses. + switch ($location['street']) { + case 'Unnamed Road': + $location['street'] = getPublicTranslation('directions'); + break; + } + + // Return the location array. + return $location; + + // OpenStreetMap lookup? + } elseif($config->OSM_LOOKUP) { + // Init defaults. + $location = []; + $location['street'] = ''; + $location['street_number'] = ''; + $location['postal_code'] = ''; + $location['district'] = ''; + + // Set maps geocode url. + $language = strtolower($config->LANGUAGE_PUBLIC); + $url = $config->OSM_URL . '/reverse?lat=' . $lat . '&lon=' . $lon . '&format=json&accept-language=' . $language; + + // Curl request. + $curl = curl_init($url); + curl_setopt($curl, CURLOPT_HTTPHEADER, array("User-Agent: PokemonRaidBot")); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + + // Proxy server? + // Use Proxyserver for curl if configured + if ($config->CURL_USEPROXY) { + curl_setopt($curl, CURLOPT_PROXY, $config->CURL_PROXYSERVER); + } + + // Curl response. + $json_response = curl_exec($curl); + + // Write request and response to log. + debug_log($url, 'OSM>'); + debug_log($json_response, ' diff --git a/logic/get_formatted_pokemon_cp.php b/logic/get_formatted_pokemon_cp.php index a447c68d..3749843a 100644 --- a/logic/get_formatted_pokemon_cp.php +++ b/logic/get_formatted_pokemon_cp.php @@ -1,39 +1,23 @@ 0) ? $row['min_cp'] : ''; + $cp20 .= (!empty($cp20) && $cp20 > 0) ? ('/' . $row['max_cp']) : ($row['max_cp']); - while($row = $rs->fetch()) { - // CP - $cp20 .= ($row['min_cp'] > 0) ? $row['min_cp'] : ''; - $cp20 .= (!empty($cp20) && $cp20 > 0) ? ('/' . $row['max_cp']) : ($row['max_cp']); - - // Weather boosted CP - $cp25 .= ($row['min_weather_cp'] > 0) ? $row['min_weather_cp'] : ''; - $cp25 .= (!empty($cp25) && $cp25 > 0) ? ('/' . $row['max_weather_cp']) : ($row['max_weather_cp']); - } - } + // Weather boosted CP + $cp25 .= ($row['min_weather_cp'] > 0) ? $row['min_weather_cp'] : ''; + $cp25 .= (!empty($cp25) && $cp25 > 0) ? ('/' . $row['max_weather_cp']) : ($row['max_weather_cp']); // Combine CP and weather boosted CP $text = ($override_language == true) ? (getPublicTranslation('pokedex_cp')) : (getTranslation('pokedex_cp')); @@ -42,5 +26,3 @@ function get_formatted_pokemon_cp($pokemon_id, $pokemon_form_id, $override_langu return $cp; } - -?> diff --git a/logic/get_gym.php b/logic/get_gym.php index e75066bf..407a76a3 100644 --- a/logic/get_gym.php +++ b/logic/get_gym.php @@ -6,18 +6,15 @@ */ function get_gym($id) { - // Get gym from database - $rs = my_query( - " - SELECT * - FROM gyms - WHERE id = {$id} - " - ); + // Get gym from database + $rs = my_query(' + SELECT * + FROM gyms + WHERE id = ? + ', [$id] + ); - $gym = $rs->fetch(); + $gym = $rs->fetch(); - return $gym; + return $gym; } - -?> diff --git a/logic/get_gym_by_telegram_id.php b/logic/get_gym_by_telegram_id.php index d8d77e44..70e98b64 100644 --- a/logic/get_gym_by_telegram_id.php +++ b/logic/get_gym_by_telegram_id.php @@ -1,25 +1,22 @@ fetch(); + $gym = $rs->fetch(); - return $gym; + return $gym; } - -?> diff --git a/logic/get_gym_details.php b/logic/get_gym_details.php index 3ecdc4a0..90159864 100644 --- a/logic/get_gym_details.php +++ b/logic/get_gym_details.php @@ -1,4 +1,5 @@ ' . getTranslation('gym_details') . ':' . CR . CR; - $msg .= 'ID = ' . $gym['id'] . '' . CR; - $msg .= getTranslation('gym') . ':' . SP; - $ex_raid_gym_marker = (strtolower($config->RAID_EX_GYM_MARKER) == 'icon') ? EMOJI_STAR : '' . $config->RAID_EX_GYM_MARKER . ''; - $msg .= ($gym['ex_gym'] ? $ex_raid_gym_marker . SP : '') . '' . $gym['gym_name'] . ''; - $msg .= CR; - // Add maps link to message. - if (!empty($gym['address'])) { - $msg .= mapslink($gym) . CR; - } else { - // Get the address. - $addr = get_address($gym['lat'], $gym['lon']); - $address = format_address($addr); + global $config; + // Add gym name to message. + $msg = '' . getTranslation('gym_details') . ':' . CR . CR; + $msg .= getTranslation('gym') . ':' . SP; + $ex_raid_gym_marker = (strtolower($config->RAID_EX_GYM_MARKER) == 'icon') ? EMOJI_STAR : '' . $config->RAID_EX_GYM_MARKER . ''; + $msg .= ($gym['ex_gym'] ? $ex_raid_gym_marker . SP : '') . '' . $gym['gym_name'] . ''; + $msg .= CR; + if($extended) $msg .= getTranslation('gym_stored_address') . CR; + // Add maps link to message. + $address = ''; + $lookupAddress = format_address(get_address($gym['lat'], $gym['lon'])); + if(!empty($gym['address'])) { + $address = $gym['address']; + } elseif(!$extended) { + $address = $lookupAddress; + } - //Only store address if not empty - if(!empty($address)) { - //Use new address - $msg .= mapslink($gym,$address) . CR; - } else { - //If no address is found show maps link - $msg .= mapslink($gym,'1') . CR; - } - } + //Only store address if not empty + if(!empty($address)) { + //Use new address + $msg .= mapslink($gym, $address) . CR; + } elseif(!$extended) { + //If no address is found show maps link + $msg .= mapslink($gym, '1') . CR; + }else { + $msg .= getTranslation('none') . CR; + } + if($extended) { + $msg .= getTranslation('gym_address_lookup_result') . ': ' . CR; + $msg .= mapslink($gym, $lookupAddress) . CR; + } - // Add or hide gym note. - if(!empty($gym['gym_note'])) { - $msg .= EMOJI_INFO . SP . $gym['gym_note']; - } + // Add or hide gym note. + if(!empty($gym['gym_note'])) { + $msg .= EMOJI_INFO . SP . $gym['gym_note'] . CR; + } - // Get extended gym details? - if($extended == true) { - $msg .= CR . '' . getTranslation('extended_gym_details') . ''; - // Normal gym? - if($gym['ex_gym'] == 1) { - $msg .= CR . '-' . SP . getTranslation('ex_gym'); - } + // Get extended gym details? + if(!$extended) + return $msg; - // Hidden gym? - if($gym['show_gym'] == 1 && $gym['ex_gym'] == 0) { - $msg .= CR . '-' . SP . getTranslation('normal_gym'); - } else if($gym['show_gym'] == 0) { - $msg .= CR . '-' . SP . getTranslation('hidden_gym'); - } - } + $msg .= CR . '' . getTranslation('extended_gym_details') . ''; + // Hidden gym? + $translation = 'hidden_gym'; + if($gym['show_gym'] == 1) { + // Normal gym? + $translation = ($gym['ex_gym'] == 1) ? 'ex_gym' : 'normal_gym'; + } + $msg .= CR . '-' . SP . getTranslation($translation); + $msg .= CR . '-' . SP . getTranslation('gym_coordinates') . ': ' . (float)$gym['lat'] . ',' . (float)$gym['lon'].''; - return $msg; + return $msg; } - -?> diff --git a/logic/get_local_pokemon_name.php b/logic/get_local_pokemon_name.php index 2d8de959..f5118775 100644 --- a/logic/get_local_pokemon_name.php +++ b/logic/get_local_pokemon_name.php @@ -1,53 +1,48 @@ fetch(); - $pokemon_form_name = $res['pokemon_form_name'] ?? 'normal'; + global $config, $botUser; + $q = my_query('SELECT pokemon_name, pokemon_form_name FROM pokemon WHERE pokedex_id = ? AND pokemon_form_id = ?', [$pokemon_id, $pokemon_form_id]); + $res = $q->fetch(); + $pokemon_form_name = $res['pokemon_form_name'] ?? 'normal'; - debug_log('Pokemon_form: ' . $pokemon_form_name); - - // Get translation type - if($override_language == true) { - $getTypeTranslation = 'getPublicTranslation'; - } else { - $getTypeTranslation = 'getTranslation'; - } - // Init pokemon name and define fake pokedex ids used for raid eggs - $pokemon_name = ''; - $eggs = $GLOBALS['eggs']; + debug_log('Pokemon_form: ' . $pokemon_form_name); + if($language === null) $language = $botUser->userLanguage; - // Get eggs from normal translation. - if(in_array($pokemon_id, $eggs)) { - $pokemon_name = $getTypeTranslation('egg_' . substr($pokemon_id, -1)); - } else { - $pokemon_name = $getTypeTranslation('pokemon_id_' . $pokemon_id); - } - if ($pokemon_form_name != 'normal') { - $pokemon_form_name = $getTypeTranslation('pokemon_form_' . $pokemon_form_name); - } - // Fallback 1: Valid pokedex id or just a raid egg? - if($pokemon_id === "NULL" || $pokemon_id == 0) { - $pokemon_name = $getTypeTranslation('egg_0'); + // Init pokemon name and define fake pokedex ids used for raid eggs + $pokemon_name = ''; - // Fallback 2: Get original pokemon name and/or form name from database - } else if(empty($pokemon_name) or empty($pokemon_form_name)) { - // Pokemon name - $pokemon_name = (empty($pokemon_name)?$res['pokemon_name']:$pokemon_name); - // Pokemon form - if(empty($pokemon_form_name) && $res['pokemon_form_name'] != 'normal') { - $pokemon_form_name = ucfirst(str_replace("_"," ",$res['pokemon_form_name'])); - } - } + // Get eggs from normal translation. + $pokemon_name = (in_array($pokemon_id, EGGS)) ? + getTranslation('egg_' . str_replace('999', '', $pokemon_id), $language) : + getTranslation('pokemon_id_' . $pokemon_id, $language); - return $pokemon_name . ($pokemon_form_name != "normal" ? " " . $pokemon_form_name : ""); + $skipFallback = false; + if ($pokemon_form_name != 'normal') { + $pokemon_form_name = getTranslation('pokemon_form_' . $pokemon_form_id, $language); + // Use only form name if form name contains Pokemon name + // e.g. Black Kyurem, Frost Rotom + if(strpos($pokemon_form_name, $pokemon_name, 0)) { + $pokemon_name = $pokemon_form_name; + $pokemon_form_name = ''; + $skipFallback = true; + } + } + // If we didn't find Pokemon name or form name from translation files, use the name from database as fallback + if(empty($pokemon_name) or empty($pokemon_form_name) && !$skipFallback) { + // Pokemon name + $pokemon_name = (empty($pokemon_name) ? $res['pokemon_name'] : $pokemon_name); + // Pokemon form + if(empty($pokemon_form_name) && $res['pokemon_form_name'] != 'normal') { + $pokemon_form_name = ucfirst(str_replace('_',' ',$res['pokemon_form_name'])); + } + } + return $pokemon_name . ($pokemon_form_name != "normal" ? " " . $pokemon_form_name : ""); } - -?> diff --git a/logic/get_overview.php b/logic/get_overview.php index 2db0c738..966ed576 100644 --- a/logic/get_overview.php +++ b/logic/get_overview.php @@ -1,110 +1,120 @@ ' . getPublicTranslation('raid_overview_for_chat') . ' ' . $chat_title . ' ' . getPublicTranslation('from') . ' '. dt2time('now') . '' . CR . CR; + $msg = '' . getPublicTranslation('raid_overview_for_chat') . ' ' . $chat_title . ' ' . getPublicTranslation('from') . ' '. dt2time('now') . '' . CR . CR; - $now = utcnow(); - - if(count($active_raids) == 0) { - $msg .= getPublicTranslation('no_active_raids') . CR . CR; - }else { - foreach($active_raids as $row) { - // Set variables for easier message building. - $raid_id = $row['id']; - $resolved_boss = resolve_raid_boss($row['pokemon'], $row['pokemon_form'], $row['spawn'], $row['level']); - $row['pokemon'] = $resolved_boss['pokedex_id']; - $row['pokemon_form'] = $resolved_boss['pokemon_form_id']; - $pokemon = get_local_pokemon_name($row['pokemon'], $row['pokemon_form'], true); - $gym = $row['gym_name']; - $ex_gym = $row['ex_gym']; - $ex_raid_gym_marker = (strtolower($config->RAID_EX_GYM_MARKER) == 'icon') ? EMOJI_STAR : '' . $config->RAID_EX_GYM_MARKER . ''; - $start_time = $row['start_time']; - $time_left = $row['t_left']; + if(count($active_raids) == 0) { + $msg .= getPublicTranslation('no_active_raids') . CR . CR; + if($config->RAID_BOSS_LIST) { + $msg .= createRaidBossList() . CR . CR; + } + //Add custom message from the config. + if (!empty($config->RAID_PIN_MESSAGE)) { + $msg .= $config->RAID_PIN_MESSAGE; + } + return $msg; + } + $now = utcnow(); + foreach($active_raids as $row) { + // Set variables for easier message building. + $raid_id = $row['id']; + $resolved_boss = resolve_raid_boss($row['pokemon'], $row['pokemon_form'], $row['spawn'], $row['level']); + $row['pokemon'] = $resolved_boss['pokedex_id']; + $row['pokemon_form'] = $resolved_boss['pokemon_form_id']; + $pokemon = get_local_pokemon_name($row['pokemon'], $row['pokemon_form'], $config->LANGUAGE_PUBLIC); + $gym = $row['gym_name']; + $ex_raid_gym_marker = (strtolower($config->RAID_EX_GYM_MARKER) == 'icon') ? EMOJI_STAR : '' . $config->RAID_EX_GYM_MARKER . ''; + $start_time = $row['start_time']; + $time_left = $row['t_left']; - debug_log($pokemon . '@' . $gym . ' found for overview.'); - // Build message and add each gym in this format - link gym_name to raid poll chat_id + message_id if possible - /* Example: - * Raid Overview from 18:18h - * - * Train Station Gym - * Raikou - still 0:24h - * - * Bus Station Gym - * Level 5 Egg 18:41 to 19:26 - */ - // Gym name. - $msg .= $ex_gym ? $ex_raid_gym_marker . SP : ''; - $msg .= !empty($chat_username) ? '' . htmlspecialchars($gym) . '' : $gym; - $msg .= CR; + debug_log($pokemon . '@' . $gym . ' found for overview.'); + // Build message and add each gym in this format - link gym_name to raid poll chat_id + message_id if possible + /* Example: + * Raid Overview from 18:18h + * + * Train Station Gym + * Raikou - still 0:24h + * + * Bus Station Gym + * Level 5 Egg 18:41 to 19:26 + */ + // Gym name. + $msg .= $row['ex_gym'] ? $ex_raid_gym_marker . SP : ''; + $msg .= !empty($chat_username) ? '' . htmlspecialchars($gym) . '' : $gym; + $msg .= CR; - if(isset($row['event_name']) && $row['event_name'] != "") { - $msg .= "" . $row['event_name'] . "" . CR; - } + if(isset($row['event_name']) && $row['event_name'] != '') { + $msg .= '' . $row['event_name'] . '' . CR; + } - // Raid has not started yet - adjust time left message - if ($now < $start_time) { - $msg .= get_raid_times($row, true); - // Raid has started already - } else { - // Add time left message. - $msg .= $pokemon . ' — ' . getPublicTranslation('still') . SP . $time_left . 'h' . CR; - } - $exclude_pokemon_sql = ""; - if(!in_array($row['pokemon'], $GLOBALS['eggs'])) { - $exclude_pokemon_sql = 'AND (pokemon = \''.$row['pokemon'].'-'.$row['pokemon_form'].'\' or pokemon = \'0\')'; - } - // Count attendances - $rs_att = my_query( - " - SELECT count(attend_time) AS count, - sum(want_invite = 0 && remote = 0 && can_invite = 0) + sum(case when want_invite = 0 && remote = 0 then attendance.extra_in_person else 0 end) AS count_in_person, - sum(want_invite = 0 && remote = 1 && can_invite = 0) + sum(case when want_invite = 0 && remote = 1 then attendance.extra_in_person else 0 end) AS count_remote, - sum(case when want_invite = 0 && can_invite = 0 then attendance.extra_alien else 0 end) AS extra_alien, - sum(case when want_invite = 1 && can_invite = 0 then 1 + attendance.extra_in_person else 0 end) AS count_want_invite, - sum(can_invite = 1) AS count_can_invite - FROM ( - SELECT DISTINCT attend_time, user_id, extra_in_person, extra_alien, remote, want_invite, can_invite - FROM attendance - WHERE raid_id = {$raid_id} - AND attend_time IS NOT NULL - AND ( attend_time > UTC_TIMESTAMP() or attend_time = '" . ANYTIME . "' ) - AND raid_done != 1 - AND cancel != 1 - {$exclude_pokemon_sql} - ) as attendance - LEFT JOIN users - ON attendance.user_id = users.user_id - " - ); + // Raid has not started yet - adjust time left message + if ($now < $start_time) { + $msg .= get_raid_times($row, $config->LANGUAGE_PUBLIC); + // Raid has started already + } else { + // Add time left message. + $msg .= $pokemon . ' — ' . getPublicTranslation('still') . SP . $time_left . 'h' . CR; + } + $exclude_pokemon_sql = ''; + if(!in_array($row['pokemon'], EGGS)) { + $exclude_pokemon_sql = 'AND (pokemon = \''.$row['pokemon'].'-'.$row['pokemon_form'].'\' or pokemon = \'0\')'; + } + // Count attendances + $rs_att = my_query(' + SELECT + count(attend_time) AS count, + sum(want_invite = 0 && remote = 0 && can_invite = 0) + sum(case when want_invite = 0 && remote = 0 then attendance.extra_in_person else 0 end) AS count_in_person, + sum(want_invite = 0 && remote = 1 && can_invite = 0) + sum(case when want_invite = 0 && remote = 1 then attendance.extra_in_person else 0 end) AS count_remote, + sum(case when want_invite = 0 && can_invite = 0 then attendance.extra_alien else 0 end) AS extra_alien, + sum(case when want_invite = 1 && can_invite = 0 then 1 + attendance.extra_in_person else 0 end) AS count_want_invite, + sum(can_invite = 1) AS count_can_invite + FROM ( + SELECT DISTINCT attend_time, user_id, extra_in_person, extra_alien, remote, want_invite, can_invite + FROM attendance + WHERE raid_id = ? + AND attend_time IS NOT NULL + AND ( attend_time > UTC_TIMESTAMP() or attend_time = \'' . ANYTIME . '\' ) + AND raid_done != 1 + AND cancel != 1 + ' .$exclude_pokemon_sql . ' + ) as attendance + LEFT JOIN users + ON attendance.user_id = users.user_id + ', [$raid_id] + ); - $att = $rs_att->fetch(); + $att = $rs_att->fetch(); - // Add to message. - if ($att['count'] > 0) { - $msg .= EMOJI_GROUP . ' ' . ($att['count_in_person'] + $att['count_remote'] + $att['extra_alien'] + $att['count_want_invite']) . ' — '; - $msg .= ((($att['count_can_invite']) > 0) ? EMOJI_CAN_INVITE . ($att['count_can_invite']) . ' ' : ''); - $msg .= ((($att['count_in_person']) > 0) ? EMOJI_IN_PERSON . ($att['count_in_person']) . ' ' : ''); - $msg .= ((($att['count_remote']) > 0) ? EMOJI_REMOTE . ($att['count_remote']) . ' ' : ''); - $msg .= ((($att['extra_alien']) > 0) ? EMOJI_ALIEN . ($att['extra_alien']) . ' ' : ''); - $msg .= (($att['count_want_invite'] > 0) ? EMOJI_WANT_INVITE . $att['count_want_invite'] : ''); - $msg .= CR; - } - $msg .= CR; - } - } - //Add custom message from the config. - if (!empty($config->RAID_PIN_MESSAGE)) { - $msg .= $config->RAID_PIN_MESSAGE; + if ($att['count'] == 0) { + $msg .= CR; + continue; } - return $msg; + // Add to message. + $msg .= EMOJI_GROUP . ' ' . ($att['count_in_person'] + $att['count_remote'] + $att['extra_alien'] + $att['count_want_invite']) . ' — '; + $msg .= ((($att['count_can_invite']) > 0) ? EMOJI_CAN_INVITE . ($att['count_can_invite']) . ' ' : ''); + $msg .= ((($att['count_in_person']) > 0) ? EMOJI_IN_PERSON . ($att['count_in_person']) . ' ' : ''); + $msg .= ((($att['count_remote']) > 0) ? EMOJI_REMOTE . ($att['count_remote']) . ' ' : ''); + $msg .= ((($att['extra_alien']) > 0) ? EMOJI_ALIEN . ($att['extra_alien']) . ' ' : ''); + $msg .= (($att['count_want_invite'] > 0) ? EMOJI_WANT_INVITE . $att['count_want_invite'] : ''); + $msg .= CR . CR; + } + if($config->RAID_BOSS_LIST) { + $msg .= createRaidBossList() . CR . CR; + } + //Add custom message from the config. + if (!empty($config->RAID_PIN_MESSAGE)) { + $msg .= $config->RAID_PIN_MESSAGE; + } + return $msg; } -?> diff --git a/logic/get_pokemon_by_table_id.php b/logic/get_pokemon_by_table_id.php index a917cb79..16bd49c1 100644 --- a/logic/get_pokemon_by_table_id.php +++ b/logic/get_pokemon_by_table_id.php @@ -5,13 +5,14 @@ * @return array */ function get_pokemon_by_table_id($pokemon_table_id) { - $q = my_query(" - SELECT pokedex_id, - pokemon_form_id - FROM pokemon - WHERE id = {$pokemon_table_id} - LIMIT 1 - "); - $return = $q->fetch(); - return $return; -} \ No newline at end of file + $q = my_query(' + SELECT pokedex_id, + pokemon_form_id + FROM pokemon + WHERE id = ? + LIMIT 1 + ', [$pokemon_table_id] + ); + $return = $q->fetch(); + return $return; +} diff --git a/logic/get_pokemon_cp.php b/logic/get_pokemon_cp.php deleted file mode 100644 index 0e496fd0..00000000 --- a/logic/get_pokemon_cp.php +++ /dev/null @@ -1,25 +0,0 @@ -fetch(); - - return $cp; -} - -?> diff --git a/logic/get_pokemon_form_name.php b/logic/get_pokemon_form_name.php index 64e81bd7..44705126 100644 --- a/logic/get_pokemon_form_name.php +++ b/logic/get_pokemon_form_name.php @@ -7,30 +7,32 @@ */ function get_pokemon_form_name($pokedex_id, $pokemon_form_id) { - debug_log($pokedex_id.'-'.$pokemon_form_id, 'Finding Pokemon form name for:'); + debug_log($pokedex_id.'-'.$pokemon_form_id, 'Finding Pokemon form name for:'); - $pokemon_form_name = 'normal'; - // Make sure $dex_id is numeric - if(is_numeric($pokedex_id) && is_numeric($pokemon_form_id)) { - // Get raid level from database - $rs = my_query( - " - SELECT pokemon_form_name - FROM pokemon - WHERE pokedex_id = {$pokedex_id} - AND pokemon_form_id = '{$pokemon_form_id}' - LIMIT 1 - " - ); - - $level = $rs->fetch(); - $pokemon_form_name = $level['pokemon_form_name']; + $pokemon_form_name = 'normal'; + // Make sure $dex_id is numeric + if(!is_numeric($pokedex_id) || !is_numeric($pokemon_form_id)) { + debug_log('Faulty dex_id or form_id, defaulting to normal.'); + return $pokemon_form_name; + } + // Get raid level from database + $rs = my_query(' + SELECT pokemon_form_name + FROM pokemon + WHERE pokedex_id = ? + AND pokemon_form_id = ? + LIMIT 1 + ', [$pokedex_id, $pokemon_form_id] + ); - debug_log($pokemon_form_name, 'Per db, level is:'); - } else { - debug_log('Faulty dex_id or form_id, defaulting to normal.'); - } + $level = $rs->fetch(); + if($level) { + $pokemon_form_name = $level['pokemon_form_name']; + } else { + $error = "pokemon_form_name unknown for pokedex_id: {$pokedex_id}, pokemon_form_id: {$pokemon_form_id}"; + throw new Exception($error); + } + debug_log($pokemon_form_name, 'Per db, level is:'); - return $pokemon_form_name; + return $pokemon_form_name; } -?> diff --git a/logic/get_pokemon_id_by_name.php b/logic/get_pokemon_id_by_name.php index d828f0e7..12a53c92 100644 --- a/logic/get_pokemon_id_by_name.php +++ b/logic/get_pokemon_id_by_name.php @@ -1,140 +1,142 @@ $pokemon_name, 'form_name' => '%'.$pokemon_form_name.'%'] + ); + $pokemon_form_id = 0; + if($query->rowCount() > 0) { + $res = $query->fetch(); + $pokemon_form_id = $res['pokemon_form_id']; + $pokemon_id = $res['pokedex_id']; } + // Write to log. + debug_log($pokemon_id,'P:'); + debug_log($pokemon_form_name.' (ID: '.$pokemon_form_id.')','P:'); - // Explode if delimiter was found. - $poke_name = $pokemon_name; - $poke_form = ""; - if($delimiter != '') { - $pokemon_name_form = explode($delimiter,$pokemon_name,2); - $poke_name = trim($pokemon_name_form[0]); - $poke_name = strtolower($poke_name); - $poke_form = trim($pokemon_name_form[1]); - $poke_form = strtolower($poke_form); - debug_log($poke_name,'P NAME:'); - debug_log($poke_form,'P FORM:'); - } - - // Init id and write name to search to log. - $pokemon_id = 0; - $pokemon_form = ($poke_form!="")?$poke_form:"normal"; - - // Set language - $language = USERLANGUAGE; - if($get_from_db) { - // Fetch Pokemon form ID from database - $stmt = $dbh->prepare(" - SELECT pokedex_id, pokemon_form_id - FROM pokemon - WHERE pokemon_name = :poke_name - AND pokemon_form_name = :form_name - LIMIT 1 - "); - $stmt->execute(['poke_name' => $poke_name, 'form_name' => $pokemon_form]); - $res = $stmt->fetch(); - if($stmt->rowCount() > 0) { - $pokemon_form_id = $res['pokemon_form_id']; - $pokemon_id = $res['pokedex_id']; - }else { - $pokemon_form_id = 0; - } - }else { - // Get translation file - $str = file_get_contents(CORE_LANG_PATH . '/pokemon.json'); - $json = json_decode($str, true); - $search_result = ""; - foreach($json as $title => $translations) { - // Try to find translation for userlanguage - if(isset($translations[$language]) && ucfirst($poke_name) == $translations[$language]) { - $search_result = $title; - debug_log('Translation found for lang: '.$language, 'P:'); - debug_log('Translation result: '.$title, 'P:'); - break; - // Also look for fallback in default language - }elseif(ucfirst($poke_name) == $translations[DEFAULT_LANGUAGE]) { - $search_result = $title; - } - } - if($search_result != "") { - $pokemon_id = str_replace('pokemon_id_','', $search_result); - }else { - // Debug log. - info_log('Error! Pokedex ID could not be found for pokemon with name: ' . $poke_name); - } + // Return pokemon_id and pokemon_form_id + return [$pokemon_id, $pokemon_form_id]; + } + debug_log($pokemon_name,'P:'); - // Get form. - // Works like this: Search form in language file via language, e.g. 'DE' and local form translation, e.g. 'Alola' for 'DE'. - // In additon we are searching the DEFAULT_LANGUAGE and the key name for the form name. - // Once we found the key name, e.g. 'pokemon_form_attack', get the form name 'attack' from it via str_replace'ing the prefix 'pokemon_form'. - if($pokemon_id != 0 && isset($poke_form) && !empty($poke_form) && $poke_form != 'normal') { - debug_log('Searching for pokemon form: ' . $poke_form); + // Explode pokemon name in case we have a form too. + $delimiter = ''; + if (strpos($pokemon_name, '-') !== false) { + $delimiter = '-'; + } else if (strpos($pokemon_name, ',') !== false) { + $delimiter = ','; + } else if (strpos($pokemon_name, '_') !== false) { + $delimiter = '_'; + } - // Get forms translation file - $str_form = file_get_contents(CORE_LANG_PATH . '/pokemon_forms.json'); - $json_form = json_decode($str_form, true); + // Explode if delimiter was found. + $poke_name = $pokemon_name; + $poke_form = ""; + if($delimiter != '') { + $pokemon_name_form = explode($delimiter,$pokemon_name,2); + $poke_name = trim($pokemon_name_form[0]); + $poke_name = strtolower($poke_name); + $poke_form = trim($pokemon_name_form[1]); + $poke_form = strtolower($poke_form); + debug_log($poke_name,'P NAME:'); + debug_log($poke_form,'P FORM:'); + } - // Search pokemon form in json - foreach($json_form as $key_form => $jform) { - // Stop search if we found it. - if ($jform[$language] === ucfirst($poke_form)) { - $pokemon_form = str_replace('pokemon_form_','',$key_form); - debug_log('Found pokemon form by user language: ' . $language); - break; + $pokemon_form = ($poke_form != "") ? $poke_form : "normal"; - // Try DEFAULT_LANGUAGE too. - } else if ($jform[DEFAULT_LANGUAGE] === ucfirst($poke_form)) { - $pokemon_form = str_replace('pokemon_form_','',$key_form); - debug_log('Found pokemon form by default language: ' . DEFAULT_LANGUAGE); - break; + // Set language + $language = $botUser->userLanguage; - // Try key name. - } else if ($key_form === ('pokemon_form_' . $poke_form)) { - $pokemon_form = str_replace('pokemon_form_','',$key_form); - debug_log('Found pokemon form by json key name: pokemon_form_' . $key_form); - break; - } - } - } - // Fetch Pokemon form ID from database - $stmt = $dbh->prepare(" - SELECT pokemon_form_id - FROM pokemon - WHERE pokedex_id = :pokedex_id - AND pokemon_form_name = :form_name - LIMIT 1 - "); - $stmt->execute(['pokedex_id' => $pokemon_id, 'form_name' => $pokemon_form]); - $res = $stmt->fetch(); - $pokemon_form_id = isset($res['pokemon_form_id']) ? $res['pokemon_form_id'] : ''; + // Get translation file + $str = file_get_contents(BOT_LANG_PATH . '/pokemon.json'); + $json = json_decode($str, true); + $search_result = ""; + foreach($json as $title => $translations) { + // Try to find translation for userlanguage + if(isset($translations[$language]) && ucfirst($poke_name) == $translations[$language]) { + $search_result = $title; + debug_log('Translation found for lang: '.$language, 'P:'); + debug_log('Translation result: '.$title, 'P:'); + break; + // Also look for fallback in default language + }elseif(ucfirst($poke_name) == $translations[DEFAULT_LANGUAGE]) { + $search_result = $title; } + } + if($search_result != "") { + $pokemon_id = str_replace('pokemon_id_','', $search_result); + }else { + // Debug log. + info_log('Error! Pokedex ID could not be found for pokemon with name: ' . $poke_name); + } - // Write to log. - debug_log($pokemon_id,'P:'); - debug_log($pokemon_form.' (ID: '.$pokemon_form_id.')','P:'); + // Get form. + // Works like this: Search form in language file via language, e.g. 'DE' and local form translation, e.g. 'Alola' for 'DE'. + // In additon we are searching the DEFAULT_LANGUAGE and the key name for the form name. + // Once we found the key name, e.g. 'pokemon_form_attack', get the form name 'attack' from it via str_replace'ing the prefix 'pokemon_form'. + if($pokemon_id != 0 && isset($poke_form) && !empty($poke_form) && $poke_form != 'normal') { + debug_log('Searching for pokemon form: ' . $poke_form); - // Set pokemon form. - $pokemon_id = $pokemon_id . '-' . $pokemon_form_id; + // Get forms translation file + $str_form = file_get_contents(BOT_LANG_PATH . '/pokemon_forms.json'); + $json_form = json_decode($str_form, true); - // Return pokemon_id - return $pokemon_id; -} + // Search pokemon form in json + foreach($json_form as $key_form => $jform) { + // Stop search if we found it. + if ($jform[$language] === ucfirst($poke_form)) { + $pokemon_form = str_replace('pokemon_form_','',$key_form); + debug_log('Found pokemon form by user language: ' . $language); + break; + + // Try DEFAULT_LANGUAGE too. + } else if ($jform[DEFAULT_LANGUAGE] === ucfirst($poke_form)) { + $pokemon_form = str_replace('pokemon_form_','',$key_form); + debug_log('Found pokemon form by default language: ' . DEFAULT_LANGUAGE); + break; + + // Try key name. + } else if ($key_form === ('pokemon_form_' . $poke_form)) { + $pokemon_form = str_replace('pokemon_form_','',$key_form); + debug_log('Found pokemon form by json key name: pokemon_form_' . $key_form); + break; + } + } + } + // Fetch Pokemon form ID from database + $query = my_query(' + SELECT pokemon_form_id + FROM pokemon + WHERE pokedex_id = :pokedex_id + AND pokemon_form_name = :form_name + LIMIT 1 + ', ['pokedex_id' => $pokemon_id, 'form_name' => $pokemon_form] + ); + $res = $query->fetch(); + $pokemon_form_id = isset($res['pokemon_form_id']) ? $res['pokemon_form_id'] : 0; + + // Write to log. + debug_log($pokemon_id,'P:'); + debug_log($pokemon_form.' (ID: '.$pokemon_form_id.')','P:'); -?> + // Return pokemon_id and pokemon_form_id + return [$pokemon_id, $pokemon_form_id]; +} diff --git a/logic/get_pokemon_info.php b/logic/get_pokemon_info.php index 19c575aa..295d87fe 100644 --- a/logic/get_pokemon_info.php +++ b/logic/get_pokemon_info.php @@ -5,26 +5,24 @@ * @param $pokemon_form_id * @return array */ -function get_pokemon_info($pokemon_id, $pokemon_form_id) +function get_pokemon_info($pokedex_id, $pokemon_form_id) { - /** Example: - * Raid boss: Mewtwo (#ID) - * Weather: Icons - * CP: CP values (Boosted CP values) - * Shiny: Shiny - */ - $info = ''; - $info .= getTranslation('raid_boss') . ': ' . get_local_pokemon_name($pokemon_id, $pokemon_form_id) . ' (#' . $pokemon_id . ')' . CR . CR; - $poke_raid_level = get_raid_level($pokemon_id, $pokemon_form_id); - $poke_cp = get_formatted_pokemon_cp($pokemon_id, $pokemon_form_id); - $poke_weather = get_pokemon_weather($pokemon_id, $pokemon_form_id); - $poke_shiny = get_pokemon_shiny_status($pokemon_id, $pokemon_form_id); - $info .= getTranslation('pokedex_raid_level') . ': ' . getTranslation($poke_raid_level . 'stars') . CR; - $info .= (empty($poke_cp)) ? (getTranslation('pokedex_cp') . CR) : $poke_cp . CR; - $info .= getTranslation('pokedex_weather') . ': ' . get_weather_icons($poke_weather) . CR; - $info .= (($poke_shiny == 1) ? (getTranslation('shiny')) : (getTranslation('not_shiny'))) . CR . CR; - - return $info; + $query = my_query(' + SELECT id, min_cp, max_cp, min_weather_cp, max_weather_cp, weather, shiny, + (SELECT raid_level + FROM raid_bosses + WHERE pokedex_id = :pokedex_id + AND pokemon_form_id = :pokemon_form_id + AND scheduled = 0 LIMIT 1) as raid_level + FROM pokemon + WHERE pokedex_id = :pokedex_id + AND pokemon_form_id = :pokemon_form_id + LIMIT 1 + ', [ + 'pokedex_id' => $pokedex_id, + 'pokemon_form_id' => $pokemon_form_id + ]); + $result = $query->fetch(); + if(is_array($result) && $result['raid_level'] == NULL) $result['raid_level'] = 0; + return $result; } - -?> diff --git a/logic/get_pokemon_shiny_status.php b/logic/get_pokemon_shiny_status.php deleted file mode 100644 index 0c2c9527..00000000 --- a/logic/get_pokemon_shiny_status.php +++ /dev/null @@ -1,31 +0,0 @@ -fetch(); - debug_log($shiny, 'Per db, shiny status is:'); - - return $shiny['shiny']; - } else { - return 0; - } -} - -?> diff --git a/logic/get_pokemon_weather.php b/logic/get_pokemon_weather.php deleted file mode 100644 index fa4a95b5..00000000 --- a/logic/get_pokemon_weather.php +++ /dev/null @@ -1,30 +0,0 @@ -fetch(); - - return $ww['weather']; - } else { - return 0; - } -} - -?> diff --git a/logic/get_raid.php b/logic/get_raid.php index d4f3e352..0628a464 100644 --- a/logic/get_raid.php +++ b/logic/get_raid.php @@ -4,52 +4,57 @@ * @param $raid_id * @return array */ +require_once(LOGIC_PATH . '/get_user.php'); require_once(LOGIC_PATH . '/resolve_raid_boss.php'); function get_raid($raid_id) { - global $dbh; - // Remove all non-numeric characters - $raidid = preg_replace( '/[^0-9]/', '', $raid_id ); + // Remove all non-numeric characters + $raidid = preg_replace( '/[^0-9]/', '', $raid_id ); - // Get the raid data by id. - $rs = my_query( - ' - SELECT raids.pokemon, raids.pokemon_form, raids.id, raids.user_id, raids.spawn, raids.start_time, raids.end_time, raids.gym_team, raids.gym_id, raids.level, raids.move1, raids.move2, raids.gender, raids.event, raids.costume, raids.event_note, - gyms.lat, gyms.lon, gyms.address, gyms.gym_name, gyms.ex_gym, gyms.gym_note, - users.name, users.trainername, users.nick, - events.name as event_name, events.description as event_description, events.vote_key_mode as event_vote_key_mode, events.time_slots as event_time_slots, events.raid_duration as event_raid_duration, events.hide_raid_picture as event_hide_raid_picture, events.poll_template as event_poll_template, - TIME_FORMAT(TIMEDIFF(end_time, UTC_TIMESTAMP()) + INTERVAL 1 MINUTE, \'%k:%i\') AS t_left - FROM raids - LEFT JOIN gyms - ON raids.gym_id = gyms.id - LEFT JOIN users - ON raids.user_id = users.user_id - LEFT JOIN events - ON events.id = raids.event - WHERE raids.id = '.$raidid.' - LIMIT 1 - ' - ); - // Get the row. - $raid = $rs->fetch(); + // Get the raid data by id. + $rs = my_query(' + SELECT raids.pokemon, raids.pokemon_form, raids.id, raids.user_id, raids.spawn, raids.start_time, raids.end_time, raids.gym_team, raids.gym_id, raids.level, raids.move1, raids.move2, raids.gender, raids.event, raids.costume, raids.event_note, + gyms.lat, gyms.lon, gyms.address, gyms.gym_name, gyms.ex_gym, gyms.gym_note, + users.name, users.trainername, users.nick, users.display_name, + events.name as event_name, events.description as event_description, events.vote_key_mode as event_vote_key_mode, events.time_slots as event_time_slots, events.raid_duration as event_raid_duration, events.hide_raid_picture as event_hide_raid_picture, events.pokemon_title as event_pokemon_title, events.poll_template as event_poll_template, + TIME_FORMAT(TIMEDIFF(end_time, UTC_TIMESTAMP()) + INTERVAL 1 MINUTE, \'%k:%i\') AS t_left, + IF(UTC_TIMESTAMP() > end_time, 1, 0) as raid_ended + FROM raids + LEFT JOIN gyms + ON raids.gym_id = gyms.id + LEFT JOIN users + ON raids.user_id = users.user_id + LEFT JOIN events + ON events.id = raids.event + WHERE raids.id = ? + LIMIT 1 + ', [$raidid] + ); + // Get the row. + $raid = $rs->fetch(); - // Resolve the boss id - $resolved_boss = resolve_raid_boss($raid['pokemon'], $raid['pokemon_form'], $raid['spawn'], $raid['level']); - $raid['pokemon'] = $resolved_boss['pokedex_id']; - $raid['pokemon_form'] = $resolved_boss['pokemon_form_id']; + if ($rs->rowCount() == 0) { + // Fail gracefully when a raid id is unknown + edit_message($GLOBALS['update'], getTranslation('internal_error'), []); + exit; + } + // Resolve the boss id + $resolved_boss = resolve_raid_boss($raid['pokemon'], $raid['pokemon_form'], $raid['spawn'], $raid['level']); + $raid['pokemon'] = $resolved_boss['pokedex_id']; + $raid['pokemon_form'] = $resolved_boss['pokemon_form_id']; - if (!$raid){ - $rs = my_query("SELECT * FROM raids WHERE raids.id = {$raid_id}"); - $row = json_encode($rs->fetch()); - throw new Exception("Failed to fetch raid id {$raid_id}, data we do have on it: {$row}"); - } + $raid['shadow'] = in_array($raid['level'], RAID_LEVEL_SHADOW) ? 1 : 0; - // Check trainername - $raid = check_trainername($raid); + if (!$raid){ + $rs = my_query('SELECT * FROM raids WHERE raids.id = ?', [$raid_id]); + $row = json_encode($rs->fetch()); + throw new Exception("Failed to fetch raid id {$raid_id}, data we do have on it: {$row}"); + } - debug_log($raid); + // Check trainername + $raid = check_trainername($raid); - return $raid; -} + debug_log($raid); -?> + return $raid; +} diff --git a/logic/get_raid_times.php b/logic/get_raid_times.php index 255589f8..6f408e6e 100644 --- a/logic/get_raid_times.php +++ b/logic/get_raid_times.php @@ -1,21 +1,14 @@ RAID_POLL_POKEMON_NAME_FIRST_LINE == true) { - $msg .= get_local_pokemon_name($raid['pokemon'], $raid['pokemon_form'], $override_language) . ':' . SP; + $msg .= get_local_pokemon_name($raid['pokemon'], $raid['pokemon_form'], $language) . ':' . SP; } else { - $msg .= $getTypeTranslation('raid') . ':' . SP; + $msg .= getTranslation('raid', $language) . ':' . SP; } } // Is the raid in the same week? @@ -67,7 +60,7 @@ function get_raid_times($raid, $override_language = true, $unformatted = false) // Adds 'at 17:00' to the output. if($unformatted == false) { - $msg .= SP . $getTypeTranslation('raid_egg_opens_at'); + $msg .= SP . getTranslation('raid_egg_opens_at', $language); } $msg .= SP . dt2time($raid['start_time']); } else { @@ -89,5 +82,3 @@ function get_raid_times($raid, $override_language = true, $unformatted = false) return $msg; } - -?> diff --git a/logic/get_remote_users_count.php b/logic/get_remote_users_count.php index 1ab72065..f5c98aa3 100644 --- a/logic/get_remote_users_count.php +++ b/logic/get_remote_users_count.php @@ -6,44 +6,48 @@ * @param $attend_time * @return int */ -function get_remote_users_count($raid_id, $user_id, $attend_time = false) +function get_remote_users_count($raidId, $userId, $attendTime = false) { - global $config; + global $config; - if(!$attend_time) { - // If attend time is not given, get the one user has already voted for from database - $att_sql = "( - SELECT attend_time - FROM attendance - WHERE raid_id = {$raid_id} - AND user_id = {$user_id} - LIMIT 1 - )"; - }else { - // Use given attend time (needed when voting for new time) - $att_sql = "'{$attend_time}'"; - } + $attBinds['userId'] = $userId; + if($attendTime) { + $attSql = ':attTime'; + $attBinds['attTime'] = $attendTime; + }else { + // If attend time is not given, get the one user has already voted for from database + $attSql = '( + SELECT attend_time + FROM attendance + WHERE raid_id = :raidId + AND user_id = :userId + LIMIT 1 + )'; + $attBinds['raidId'] = $raidId; + $attBinds['userId'] = $userId; + } - // Check if max remote users limit is already reached! - // Ignore max limit if attend time is 'Anytime' - $rs = my_query( - " - SELECT IF(attend_time = '" . ANYTIME . "', 0, sum(1 + extra_in_person)) AS remote_users - FROM (SELECT DISTINCT user_id, extra_in_person, attend_time FROM attendance WHERE remote = 1 AND cancel = 0 AND raid_done = 0) as T - WHERE attend_time = {$att_sql} - GROUP BY attend_time - " - ); + // Check if max remote users limit is already reached! + // Ignore max limit if attend time is 'Anytime' + $rs = my_query( + ' + SELECT CASE WHEN attend_time = \'' . ANYTIME . '\' + THEN 0 + ELSE + sum(CASE WHEN remote = 1 THEN 1 + extra_in_person ELSE 0 END + extra_alien) END AS remote_users + FROM (SELECT DISTINCT user_id, extra_in_person, extra_alien, remote, attend_time FROM attendance WHERE (remote = 1 or extra_alien > 0) AND cancel = 0 AND raid_done = 0 and user_id != :userId) as T + WHERE attend_time = ' . $attSql . ' + GROUP BY attend_time + ', $attBinds + ); - // Get the answer. - $answer = $rs->fetch(); - $remote_users = empty($answer) ? 0 : $answer['remote_users']; + // Get the answer. + $answer = $rs->fetch(); + $remoteUsers = empty($answer) ? 0 : $answer['remote_users']; - // Write to log. - debug_log($remote_users, 'Remote participants so far:'); - debug_log($config->RAID_REMOTEPASS_USERS_LIMIT, 'Maximum remote participants:'); + // Write to log. + debug_log($remoteUsers, 'Remote participants so far:'); + debug_log($config->RAID_REMOTEPASS_USERS_LIMIT, 'Maximum remote participants:'); - return $remote_users; + return $remoteUsers; } - -?> diff --git a/logic/get_user.php b/logic/get_user.php index 46c554a3..99bf1db4 100644 --- a/logic/get_user.php +++ b/logic/get_user.php @@ -4,65 +4,52 @@ * @param $user_id * @param $public * @param $return_row - * @return message + * @return array|string message */ function get_user($user_id, $public = true, $return_row = false) { - global $config; - // Get user details. - $rs = my_query( - " - SELECT * - FROM users - WHERE user_id = {$user_id} - " - ); + global $config; + // Get user details. + $rs = my_query(' + SELECT * + FROM users + WHERE user_id = ? + ', [$user_id] + ); + // Fetch the row. + $row = $rs->fetch(); + // Build message string. + $msg = ''; - // Fetch the row. - $row = $rs->fetch(); - // Build message string. - $msg = ''; + $display_name = ['','']; + if(!$public) $display_name[intval($row['display_name'])] = '-> '; - $display_name = ['','']; - if(!$public) $display_name[intval($row['display_name'])] = '-> '; + // Add name. + $msg .= $display_name[0] . getTranslation('name') . ': ' . htmlspecialchars($row['name']) . '' . CR; + // Add name. + $msg .= $display_name[1] . getTranslation('trainername') . ': ' . (check_for_empty_string($row['trainername']) ? getTranslation('not_set') : $row['trainername'] ) . CR; - // Add name. - $msg .= $display_name[0] . getTranslation('name') . ': ' . htmlspecialchars($row['name']) . '' . CR; - // Add name. - $msg .= $display_name[1] . getTranslation('trainername') . ': ' . (check_for_empty_string($row['trainername']) ? getTranslation('not_set') : $row['trainername'] ) . CR; + if($config->RAID_POLL_SHOW_TRAINERCODE){ // is Trainercode enabled? + $trainercode = ($row['trainercode'] === NULL ? getTranslation('not_set') : $row['trainercode']); + $msg .= getTranslation('trainercode') . ': ' . $trainercode . CR; + } - if($config->RAID_POLL_SHOW_TRAINERCODE){ // is Trainercode enabled? - // Unknown trainercode. - if ($row['trainercode'] === NULL) { - $msg .= getTranslation('trainercode') . ': ' . getTranslation('not_set') . CR; - // Known Trainercode. - } else { - $msg .= getTranslation('trainercode') . ': ' . $row['trainercode'] . CR; - } - } + $team = ($row['team'] === NULL) ? $GLOBALS['teams']['unknown'] : $GLOBALS['teams'][$row['team']]; + $msg .= getTranslation('team') . ': ' . $team . CR; - // Unknown team. - if ($row['team'] === NULL) { - $msg .= getTranslation('team') . ': ' . $GLOBALS['teams']['unknown'] . CR; - // Known team. - } else { - $msg .= getTranslation('team') . ': ' . $GLOBALS['teams'][$row['team']] . CR; - } - - // Add level. - if ($row['level'] != 0) { - $msg .= getTranslation('level') . ': ' . $row['level'] . '' . CR . CR; - } - if(!$public) $msg .= getTranslation('display_name_explanation') . CR; + // Add level. + if ($row['level'] != 0) { + $msg .= getTranslation('level') . ': ' . $row['level'] . '' . CR . CR; + } + if(!$public) $msg .= getTranslation('display_name_explanation') . CR; - if($return_row) { - return [ - 'message' => $msg, - 'row' => $row - ]; - }else { - return $msg; - } + if($return_row) { + return [ + 'message' => $msg, + 'row' => $row + ]; + } + return $msg; } /** @@ -71,22 +58,22 @@ function get_user($user_id, $public = true, $return_row = false) * @return array $row */ function check_trainername($row){ - global $config; - // if Custom Trainername is enabled by config - if($config->CUSTOM_TRAINERNAME == false || check_for_empty_string($row['trainername']) || (isset($row['display_name']) && $row['display_name'] != 1)){ // trainername not set by user - // check if Telegram-@Nick is set - if(!check_for_empty_string($row['nick']) && $config->RAID_POLL_SHOW_NICK_OVER_NAME){ - // set Telegram-@Nick as Name inside the bot - $row['name'] = $row['nick']; - }else{ - // leave Telegram-name as it is (Trainername and Telegram-@Nick were not configured by user) - } + global $config; + // if Custom Trainername is enabled by config + if($config->CUSTOM_TRAINERNAME == false || check_for_empty_string($row['trainername']) || $row['display_name'] != 1){ // trainername not set by user + // check if Telegram-@Nick is set + if(!check_for_empty_string($row['nick']) && $config->RAID_POLL_SHOW_NICK_OVER_NAME){ + // set Telegram-@Nick as Name inside the bot + $row['name'] = $row['nick']; }else{ - // Trainername is configured by User - $row['name'] = $row['trainername']; + // leave Telegram-name as it is (Trainername and Telegram-@Nick were not configured by user) } + }else{ + // Trainername is configured by User + $row['name'] = $row['trainername']; + } - return $row; + return $row; } /** @@ -100,4 +87,3 @@ function check_for_empty_string($string){ } return false; } -?> diff --git a/logic/get_weather_icons.php b/logic/get_weather_icons.php index 1d6461f6..7b642ba1 100644 --- a/logic/get_weather_icons.php +++ b/logic/get_weather_icons.php @@ -6,28 +6,23 @@ */ function get_weather_icons($weather_value) { - if($weather_value > 0) { - // Get length of arg and split arg - $weather_value_length = strlen((string)$weather_value); - $weather_value_string = str_split((string)$weather_value); + if($weather_value == 0) return ''; + // Get length of arg and split arg + $weather_value_length = strlen((string)$weather_value); + $weather_value_string = str_split((string)$weather_value); - // Init weather icons string. - $weather_icons = ''; + // Init weather icons string. + $weather_icons = ''; - // Add icons to string. - for ($i = 0; $i < $weather_value_length; $i = $i + 1) { - // Get weather icon from constants - $weather_icons .= $GLOBALS['weather'][$weather_value_string[$i]]; - $weather_icons .= ' '; - } + // Add icons to string. + for ($i = 0; $i < $weather_value_length; $i = $i + 1) { + // Get weather icon from constants + $weather_icons .= $GLOBALS['weather'][$weather_value_string[$i]]; + $weather_icons .= ' '; + } - // Trim space after last icon - $weather_icons = rtrim($weather_icons); - } else { - $weather_icons = ''; - } + // Trim space after last icon + $weather_icons = rtrim($weather_icons); - return $weather_icons; + return $weather_icons; } - -?> diff --git a/logic/group_code_keys.php b/logic/group_code_keys.php index 82b14723..6974311e 100644 --- a/logic/group_code_keys.php +++ b/logic/group_code_keys.php @@ -8,61 +8,48 @@ */ function group_code_keys($raid_id, $action, $arg) { - global $config; - - // Get current group code - $data = explode("-", $arg); - $poke1 = $data[0]; - $poke2 = $data[1]; - $poke3 = $data[2]; - $code_action = $data[3]; - - // Send and reset values - $reset_arg = '0-0-0-add'; - $send_arg = $poke1 . '-' . $poke2 . '-' . $poke3 . '-send'; - - // Init empty keys array. - $keys = []; - - // Show group code buttons? - if($poke3 == 0) { - - // Add keys 1 to 9, where 1 = first pokemon, 9 = last pokemon - /** - * 1 2 3 - * 4 5 6 - * 7 8 9 - */ - - $rc_poke = (explode(',',$config->RAID_CODE_POKEMON)); - foreach($rc_poke as $i) { - // New code - $new_code = ($poke1 == 0) ? ($i . '-0-0-add') : (($poke2 == 0) ? ($poke1 . '-' . $i . '-0-add') : (($poke3 == 0) ? ($poke1 . '-' . $poke2 . '-' . $i . '-add') : ($poke1 . '-' . $poke2 . '-' . $poke3 . '-send'))); - // Set keys. - $keys[] = array( - 'text' => get_local_pokemon_name($i, '0'), - 'callback_data' => $raid_id . ':' . $action . ':' . $new_code - ); - } - } else { - // Send - $keys[] = array( - 'text' => EMOJI_INVITE, - 'callback_data' => $raid_id . ':' . $action . ':' . $send_arg - ); + global $config; + + // Get current group code + $data = explode('-', $arg); + $poke1 = $data[0]; + $poke2 = $data[1]; + $poke3 = $data[2]; + + // Send and reset values + $reset_arg = '0-0-0-add'; + $send_arg = $poke1 . '-' . $poke2 . '-' . $poke3 . '-send'; + + // Init empty keys array. + $keys = []; + + // Show group code buttons? + if($poke3 == 0) { + + // Add keys 1 to 9, where 1 = first pokemon, 9 = last pokemon + /** + * 1 2 3 + * 4 5 6 + * 7 8 9 + */ + + $rc_poke = (explode(',',$config->RAID_CODE_POKEMON)); + foreach($rc_poke as $i) { + // New code + $new_code = ($poke1 == 0) ? ($i . '-0-0-add') : (($poke2 == 0) ? ($poke1 . '-' . $i . '-0-add') : (($poke3 == 0) ? ($poke1 . '-' . $poke2 . '-' . $i . '-add') : ($poke1 . '-' . $poke2 . '-' . $poke3 . '-send'))); + // Set keys. + $keys[] = button(get_local_pokemon_name($i, '0'), [$action, 'r' => $raid_id, 'a' => $new_code]); } + } else { + // Send + $keys[] = button(EMOJI_INVITE, [$action, 'r' => $raid_id, 'a' => $send_arg]); + } - // Reset - $keys[] = array( - 'text' => getTranslation('reset'), - 'callback_data' => $raid_id . ':' . $action . ':' . $reset_arg - ); + // Reset + $keys[] = button(getTranslation('reset'), [$action, 'r' => $raid_id, 'a' => $reset_arg]); - // Get the inline key array. - $keys = inline_key_array($keys, 3); + // Get the inline key array. + $keys = inline_key_array($keys, 3); - return $keys; + return $keys; } - - -?> diff --git a/logic/gymMenu.php b/logic/gymMenu.php new file mode 100644 index 00000000..f97ab654 --- /dev/null +++ b/logic/gymMenu.php @@ -0,0 +1,285 @@ + 'edit_raidlevel', + 'list' => 'list_raid', + 'gym' => 'gym_edit_details', +]; + +function resolveDefaultGymarea($userId) { + global $config; + if(!$config->ENABLE_GYM_AREAS) return false; + $q = my_query('SELECT gymarea FROM users WHERE user_id = ? LIMIT 1', [$userId]); + $userGymarea = $q->fetch()['gymarea']; + return $userGymarea !== NULL ? $userGymarea : $config->DEFAULT_GYM_AREA; +} +/** + * Raid gym first letter selection + * @param string $buttonAction Action that is performed by gym letter keys + * @param bool $showHidden Show only hidden gyms? + * @param int $stage + * @param string $firstLetter + * @param int|false $gymareaId + * @return array + */ +function gymMenu($buttonAction, $showHidden, $stage, $firstLetter = false, $gymareaId = false) { + + global $config, $botUser; + // Stage 0: Only gym area keys + // Stage 1: Gym letter keys (or just gym names if 20 or less gyms were found) with areas under them + // Stage 2: Gym names + $stage = ($config->ENABLE_GYM_AREAS && $stage == 1 && $gymareaId == false) ? 0 : $stage; + [$gymareaName, $gymareaKeys, $gymareaQuery] = ($config->ENABLE_GYM_AREAS) ? getGymareas($gymareaId, $stage, $buttonAction) : ['', [], '']; + if($stage == 2 && $firstLetter != '') + $gymKeys = createGymListKeysByFirstLetter($firstLetter, $showHidden, $gymareaQuery, $buttonAction, $gymareaId); + else + $gymKeys = createGymKeys($buttonAction, $showHidden, $gymareaId, $gymareaQuery, $stage); + $keys = ($stage == 0) ? [] : $gymKeys[0]; + if($stage == 0) { + $title = getTranslation('select_gym_area'); + }elseif($stage == 1) { + if($config->ENABLE_GYM_AREAS) { + $title = $gymKeys[1] === true ? getTranslation('select_gym_first_letter_or_gym_area') : getTranslation('select_gym_name_or_gym_area'); + }else { + $title = $gymKeys[1] === true ? getTranslation('select_gym_first_letter') : getTranslation('select_gym_name'); + } + }else { + $title = getTranslation('select_gym_name'); + } + $gymareaTitle = '' . $title . '' . CR; + $gymareaTitle.= ($gymareaName != '') ? CR . CR . getTranslation('current_gymarea') . ': ' . $gymareaName : ''; + + $gymareaTitle.= ($config->RAID_VIA_LOCATION && $buttonAction == 'create' ? (CR . CR . getTranslation('send_location')) : ''); + + if($config->RAID_VIA_LOCATION_FUNCTION == 'remote' && $buttonAction == 'list') { + $query_remote = my_query('SELECT count(*) as count FROM raids LEFT JOIN gyms on raids.gym_id = gyms.id WHERE raids.end_time > (UTC_TIMESTAMP() - INTERVAL 10 MINUTE) AND temporary_gym = 1'); + if($query_remote->fetch()['count'] > 0) { + $keys[][] = button(getTranslation('remote_raids'), 'list_remote_gyms'); + } + } + // Merge keys. + if(($stage < 2 or ($stage == 2 && $firstLetter == '')) && $showHidden == 0) { + $keys = array_merge($keys, inline_key_array($gymareaKeys, 2)); + } + // Add key for hidden gyms. + if($buttonAction == 'gym') { + if($stage == 1 && $showHidden == 0) { + // Add key for hidden gyms. + $h_keys[][] = button(getTranslation('hidden_gyms'), ['gymMenu', 'h' => 1, 'a' => 'gym', 'ga' => $gymareaId]); + $keys = array_merge($h_keys, $keys); + } + if($stage == 0 or $stage == 1 && $botUser->accessCheck('gym-add', true)) { + $keys[][] = button(getTranslation('gym_create'), 'gym_create'); + } + } + if((($stage == 1 or ($stage == 2 && $firstLetter == '')) && $config->DEFAULT_GYM_AREA === false)) { + $backKey = button(getTranslation('back'), ['gymMenu', 'stage' => 0, 'a' => $buttonAction]); + }elseif($stage == 2 && $firstLetter !== '') { + $backKey = button(getTranslation('back'), ['gymMenu', 'stage' => 1, 'a' => $buttonAction, 'h' => $showHidden, 'ga' => $gymareaId]); + } + $abortKey = button(getTranslation('abort'), 'exit'); + if (isset($backKey)) { + $keys[] = [$backKey, $abortKey]; + }else{ + $keys[] = [$abortKey];} + return ['keys' => $keys, 'gymareaTitle' => $gymareaTitle]; +} + +/** + * @param int $gymareaId + * @param int $stage + * @param string $buttonAction + * @return array [$gymareaName, $gymareaKeys, $query] + */ +function getGymareas($gymareaId, $stage, $buttonAction) { + $gymareaKeys = $points = []; + $gymareaName = ''; + $json = json_decode(file_get_contents(botSpecificConfigFile('geoconfig_gym_areas.json')), 1); + foreach($json as $area) { + if($gymareaId == $area['id']) { + foreach($area['path'] as $point) { + $points[] = $point[0].' '.$point[1]; + } + $gymareaName = $area['name']; + if($points[0] != $points[count($points)-1]) $points[] = $points[0]; + } + if ($stage != 0 && $gymareaId == $area['id']) continue; + $gymareaKeys[] = button($area['name'], ['gymMenu', 'a' => $buttonAction, 'stage' => 1, 'ga' => $area['id']]); + + } + if(count($gymareaKeys) > 6 && $stage != 0) { + // If list of area buttons is getting too large, replace it with a key that opens a submenu + $gymareaKeys[] = button(getTranslation('gymareas'), ['gymMenu', 'a' => $buttonAction, 'stage' => 0]); + } + $polygon_string = implode(',', $points); + $query = count($points) > 0 ? 'AND ST_CONTAINS(ST_GEOMFROMTEXT(\'POLYGON(('.$polygon_string.'))\'), ST_GEOMFROMTEXT(CONCAT(\'POINT(\',lat,\' \',lon,\')\')))' : ''; + return [$gymareaName, $gymareaKeys, $query]; +} + +/** + * @param string $buttonAction + * @param bool $showHidden + * @param int $gymareaId + * @param string $gymareaQuery + * @param int $stage + * @return array [keyArray, isArrayListOfLetters] + */ +function createGymKeys($buttonAction, $showHidden, $gymareaId, $gymareaQuery, $stage) { + global $config, $menuActions, $botUser; + // Show hidden gyms? + $show_gym = $showHidden ? 0 : 1; + + if ($buttonAction == 'list') { + // Select only gyms with active raids + $queryConditions = ' + LEFT JOIN raids + ON raids.gym_id = gyms.id + WHERE show_gym = ' . $show_gym . ' + AND end_time > UTC_TIMESTAMP() '; + $eventQuery = 'event IS NULL'; + if($botUser->accessCheck('ex-raids', true)) { + if($botUser->accessCheck('event-raids', true)) + $eventQuery = ''; + else + $eventQuery .= ' OR event = ' . EVENT_ID_EX; + }elseif($botUser->accessCheck('event-raids', true)) { + $eventQuery = 'event != ' . EVENT_ID_EX .' OR event IS NULL'; + } + $eventQuery = ($eventQuery == '') ? ' ' : ' AND ('.$eventQuery.') '; + }else { + $eventQuery = ' '; + $queryConditions = ' WHERE show_gym = ' . $show_gym; + } + $rs_count = my_query('SELECT COUNT(gym_name) as count FROM gyms ' . $queryConditions . $eventQuery . $gymareaQuery); + $gym_count = $rs_count->fetch(); + + // Found 20 or less gyms, print gym names + if($gym_count['count'] <= 20) { + $keys = createGymListKeysByFirstLetter('', $showHidden, $gymareaQuery, $buttonAction, $gymareaId); + return $keys; + } + + // If found over 20 gyms, print letters + $select = 'SELECT DISTINCT UPPER(SUBSTR(gym_name, 1, 1)) AS first_letter'; + $group_order = ' ORDER BY 1'; + // Special/Custom gym letters? + if(!empty($config->RAID_CUSTOM_GYM_LETTERS)) { + // Explode special letters. + $special_keys = explode(',', $config->RAID_CUSTOM_GYM_LETTERS); + $select = 'SELECT CASE '; + foreach($special_keys as $letter) + { + $letter = trim($letter); + debug_log($letter, 'Special gym letter:'); + // Fix chinese chars, prior: $length = strlen($letter); + $length = strlen(mb_convert_encoding($letter, 'ISO-8859-1')); + $select .= SP . 'WHEN UPPER(LEFT(gym_name, ' . $length . ')) = \'' . $letter . '\' THEN UPPER(LEFT(gym_name, ' . $length . '))' . SP; + } + $select .= 'ELSE UPPER(LEFT(gym_name, 1)) END AS first_letter'; + $group_order = ' GROUP BY 1 ORDER BY gym_name'; + } + $rs = my_query( + $select . + ' FROM gyms ' . + $queryConditions . ' ' . + $gymareaQuery . + $group_order + ); + while ($gym = $rs->fetch()) { + // Add first letter to keys array + $keys[] = button($gym['first_letter'], ['gymMenu', 'a' => $buttonAction, 'stage' => $stage+1, 'fl' => $gym['first_letter'], 'h' => $showHidden, 'ga' => $gymareaId]); + } + + // Get the inline key array. + return [inline_key_array($keys, 4), true]; +} +/** + * Raid edit gym keys with active raids marker. + * @param string $firstLetter + * @param bool $showHidden + * @param string $gymareaQuery + * @param string $buttonAction + * @return array + */ +function createGymListKeysByFirstLetter($firstLetter, $showHidden, $gymareaQuery = '', $buttonAction = '', $gymareaId = false) { + global $config, $menuActions, $botUser; + // Length of first letter. + // Fix chinese chars, prior: $first_length = strlen($first); + $first_length = strlen(mb_convert_encoding($firstLetter, 'ISO-8859-1')); + + // Special/Custom gym letters? + $not = ''; + if(!empty($config->RAID_CUSTOM_GYM_LETTERS) && $first_length == 1) { + // Explode special letters. + $special_keys = explode(',', $config->RAID_CUSTOM_GYM_LETTERS); + + foreach($special_keys as $letter) + { + $letter = trim($letter); + debug_log($letter, 'Special gym letter:'); + // Fix chinese chars, prior: $length = strlen($letter); + $length = strlen(mb_convert_encoding($letter, 'ISO-8859-1')); + $not .= SP . 'AND UPPER(LEFT(gym_name, ' . $length . ')) != UPPER(\'' . $letter . '\')' . SP; + } + } + $show_gym = $showHidden ? 0 : 1; + + $eventQuery = 'event IS NULL'; + if($botUser->accessCheck('ex-raids', true)) { + if($botUser->accessCheck('event-raids', true)) + $eventQuery = ''; + else + $eventQuery .= ' OR event = ' . EVENT_ID_EX; + }elseif($botUser->accessCheck('event-raids', true)) { + $eventQuery = 'event != ' . EVENT_ID_EX .' OR event IS NULL'; + } + $eventQuery = ($eventQuery == '') ? ' ' : ' AND ('.$eventQuery.') '; + + $letterQuery = ($firstLetter != '') ? 'AND UPPER(LEFT(gym_name, ' . $first_length . ')) = UPPER(\'' . $firstLetter . '\')' : ''; + + $query_collate = ($config->MYSQL_SORT_COLLATE != '') ? 'COLLATE ' . $config->MYSQL_SORT_COLLATE : ''; + // Get gyms from database + $rs = my_query(' + SELECT gyms.id, gyms.gym_name, gyms.ex_gym, + case when (select 1 from raids where gym_id = gyms.id and end_time > utc_timestamp() '.$eventQuery.' LIMIT 1) = 1 then 1 else 0 end as active_raid + FROM gyms + WHERE show_gym = ? + ' . $letterQuery . ' + ' . $not . ' + ' . $gymareaQuery . ' + ORDER BY gym_name ' . $query_collate + , [$show_gym] + ); + + // Init empty keys array. + $keys = []; + + while ($gym = $rs->fetch()) { + if ($buttonAction == 'list' && $gym['active_raid'] == 0) continue; + // Show Ex-Gym-Marker? + if($config->RAID_CREATION_EX_GYM_MARKER && $gym['ex_gym'] == 1) { + $ex_raid_gym_marker = (strtolower($config->RAID_EX_GYM_MARKER) == 'icon') ? EMOJI_STAR : $config->RAID_EX_GYM_MARKER; + $gym_name = $ex_raid_gym_marker . SP . $gym['gym_name']; + } else { + $gym_name = $gym['gym_name']; + } + // Add warning emoji for active raid + if ($gym['active_raid'] == 1) { + $gym_name = EMOJI_WARN . SP . $gym_name; + } + $callback = [ + $menuActions[$buttonAction], + 'g' => $gym['id'], + 'ga' => $gymareaId, + 'fl' => $firstLetter, + 'h' => $showHidden, + ]; + $keys[] = button($gym_name, $callback); + } + + // Get the inline key array. + $keys = inline_key_array($keys, 1); + + return [$keys, false]; + +} diff --git a/logic/history.php b/logic/history.php index 2c243157..493940a1 100644 --- a/logic/history.php +++ b/logic/history.php @@ -1,50 +1,42 @@ rowcount() == 0) return false; - while($date = $q->fetch()) { - $day_keys[] = [ - 'text' => $date['day_disp'], - 'callback_data' => $date['day'] . ':history:' . $date['current_y_m'] - ]; - if($current_y_m == '') $current_y_m = $date['current_y_m']; - if($prev == false) $prev = $date['prev']; - if($next == false) $next = $date['next']; - } - $keys = inline_key_array($day_keys,4); + if(strlen($current) == 7) { + // Reformat YYYY-MM to DATETIME + $current = '\''.$current.'-01 00:00:00\''; + } + $q = my_query(' + SELECT DISTINCT DATE_FORMAT(start_time, "%d") as day, DATE_FORMAT(start_time, "%e") as day_disp, DATE_FORMAT('.$current.', "%Y-%m") as current_y_m, + if((SELECT count(*) FROM raids left join attendance on attendance.raid_id = raids.id where end_time < UTC_TIMESTAMP() and date_format(start_time, "%Y-%m") = date_format(DATE_SUB('.$current.', INTERVAL 1 MONTH), "%Y-%m") and attendance.id is not null limit 1), date_format(DATE_SUB('.$current.', INTERVAL 1 MONTH), "%Y-%m"), 0) as prev, + if((SELECT count(*) FROM raids left join attendance on attendance.raid_id = raids.id where end_time < UTC_TIMESTAMP() and date_format(start_time, "%Y-%m") = date_format(DATE_ADD('.$current.', INTERVAL 1 MONTH), "%Y-%m") and attendance.id is not null limit 1), date_format(DATE_ADD('.$current.', INTERVAL 1 MONTH), "%Y-%m"), 0) as next + FROM raids + LEFT JOIN attendance + ON attendance.raid_id = raids.id + WHERE end_time < UTC_TIMESTAMP() + AND date_format(start_time, "%Y-%m") = DATE_FORMAT('.$current.', "%Y-%m") + AND attendance.id IS NOT NULL + ORDER BY DATE_FORMAT(start_time, "%d") ASC + '); + $day_keys = []; + $current_y_m = ''; + $prev = $next = false; + if($q->rowcount() == 0) return false; + while($date = $q->fetch()) { + $day_keys[] = button($date['day_disp'], ['history', 'd' => $date['day'], 'm' => $date['current_y_m']]); + if($current_y_m == '') $current_y_m = $date['current_y_m']; + if($prev == false) $prev = $date['prev']; + if($next == false) $next = $date['next']; + } + $keys = inline_key_array($day_keys,4); - $msg = getTranslation('history_title') . CR . CR; - $msg .= getTranslation('history_displaying_month') . ' ' . getTranslation('month_'.substr($current_y_m,5)) . CR . CR . getTranslation('raid_select_date'); + $msg = getTranslation('history_title') . CR . CR; + $msg .= getTranslation('history_displaying_month') . ' ' . getTranslation('month_'.substr($current_y_m,5)) . CR . CR . getTranslation('raid_select_date'); - $nav_keys = []; - if($prev != 0) $nav_keys[0][] = ['text' => getTranslation('month_'.substr($prev,5)),'callback_data' => '0:history:' . $prev]; - if($next != 0) $nav_keys[0][] = ['text' => getTranslation('month_'.substr($next,5)),'callback_data' => '0:history:' . $next]; + $nav_keys = []; + if($prev != 0) $nav_keys[0][] = button(getTranslation('month_'.substr($prev,5)),['history', 'm' => $prev]); + if($next != 0) $nav_keys[0][] = button(getTranslation('month_'.substr($next,5)),['history', 'm' => $next]); - $keys = array_merge($keys, $nav_keys); - $keys[] = [ - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ]; - return [$msg, $keys]; + $keys = array_merge($keys, $nav_keys); + $keys[][] = button(getTranslation('abort'), 'exit'); + return [$msg, $keys]; } -?> \ No newline at end of file diff --git a/logic/insert_cleanup.php b/logic/insert_cleanup.php index b77b6fdc..6d3f04ef 100644 --- a/logic/insert_cleanup.php +++ b/logic/insert_cleanup.php @@ -1,34 +1,44 @@ 0)) { - // Build query for cleanup table to add cleanup info to database - debug_log('Adding cleanup info to database:'); - $rs = my_query( - " - INSERT INTO cleanup - SET raid_id = '{$raid_id}', - chat_id = '{$chat_id}', - message_id = '{$message_id}', - type = '{$type}' - " - ); - } else { - debug_log('Invalid input for cleanup preparation!'); - } -} + if (!is_numeric($chat_id) || !is_numeric($message_id) || !is_numeric($raid_id) || $raid_id < 1) { + debug_log('Invalid input for cleanup preparation!'); + return; + } -?> + // Build query for cleanup table to add cleanup info to database + debug_log('Adding cleanup info to database:'); + my_query(' + REPLACE INTO cleanup + SET raid_id = :raid_id, + chat_id = :chat_id, + message_id = :message_id, + thread_id = :thread_id, + type = :type, + media_unique_id = :media_unique_id + ', [ + ':raid_id' => $raid_id, + ':chat_id' => $chat_id, + ':message_id' => $message_id, + ':thread_id' => $thread_id, + ':type' => $type, + ':media_unique_id' => $photo_id, + ] + ); +} diff --git a/logic/insert_overview.php b/logic/insert_overview.php index ea79a141..425c4106 100644 --- a/logic/insert_overview.php +++ b/logic/insert_overview.php @@ -1,42 +1,55 @@ fetch(); + $row = $rs->fetch(); - // Overview already in database or new - if (empty($row['count'])) { - // Build query for overview table to add overview info to database - debug_log('Adding new overview information to database overview list!'); - $rs = my_query( - " - INSERT INTO overview - SET chat_id = '{$chat_id}', - message_id = '{$message_id}', - chat_title = '{$chat_title}', - chat_username = '{$chat_username}', - updated = DATE(NOW()) - " - ); - } else { - // Nothing to do - overview information is already in database. - debug_log('Overview information is already in database! Nothing to do...'); - } + // Overview already in database or new + if (!empty($row['count'])) { + // Nothing to do - overview information is already in database. + debug_log('Overview information is already in database! Nothing to do...'); + return; + } + // Build query for overview table to add overview info to database + debug_log('Adding new overview information to database overview list!'); + my_query( + ' + INSERT INTO overview + SET chat_id = :chat_id, + message_id = :message_id, + thread_id = :thread_id, + chat_title = :chat_title, + chat_username = :chat_username, + updated = DATE(NOW()) + ', [ + 'chat_id' => $chat_id, + 'message_id' => $message_id, + 'thread_id' => $thread_id, + 'chat_title' => $chat_title, + 'chat_username' => $chat_username, + ] + ); } - -?> diff --git a/logic/insert_trainerinfo.php b/logic/insert_trainerinfo.php index 210f0e71..351a4dd6 100644 --- a/logic/insert_trainerinfo.php +++ b/logic/insert_trainerinfo.php @@ -1,37 +1,35 @@ fetch(); + $row = $rs->fetch(); - // Trainer info already in database or new - if (empty($row['count'])) { - // Build query for trainerinfo table to add trainer info to database - debug_log('Adding new trainer information to database trainer info list!'); - $rs = my_query( - " - INSERT INTO trainerinfo - SET chat_id = '{$chat_id}', - message_id = '{$message_id}' - " - ); - } else { - // Nothing to do - trainer information is already in database. - debug_log('Trainer information is already in database! Nothing to do...'); - } + // Trainer info already in database or new + if (!empty($row['count'])) { + // Nothing to do - trainer information is already in database. + debug_log('Trainer information is already in database! Nothing to do...'); + return; + } + // Build query for trainerinfo table to add trainer info to database + debug_log('Adding new trainer information to database trainer info list!'); + my_query(' + INSERT INTO trainerinfo + SET chat_id = ?, + message_id = ?, + thread_id = ? + ', [$chat_id, $message_id, $thread_id] + ); } - -?> diff --git a/logic/key_util.php b/logic/key_util.php new file mode 100644 index 00000000..9fe14a68 --- /dev/null +++ b/logic/key_util.php @@ -0,0 +1,164 @@ += $columns) { + $row++; + $col = 0; + } + } + return $result; +} + +/** + * Share keys. Has own logic for fetching chat id's if used to generate share keys for raids. + * @param int|false $id Id to pass to callback query + * @param string $action Action to pass to callback query + * @param array $update Update from Telegram + * @param int $raidLevel Raid level if sharing a raid + * @param array $chats List of chats if using alternative list + * @param bool $hideGeneralShare Leave out the general share button + * @return array + */ +function share_keys($id, $action, $update, $raidLevel = '', $chats = [], $hideGeneralShare = false) +{ + global $config, $botUser; + $keys = []; + // Check access. + $share_access = $botUser->accessCheck('share-any-chat', true); + + // Add share button if not restricted to allow sharing to any chat. + if ($share_access == true && $hideGeneralShare == false) { + debug_log('Adding general share key to inline keys'); + // Set the keys. + $keys[][] = [ + 'text' => getTranslation('share'), + 'switch_inline_query' => basename(ROOT_PATH) . ':' . strval($id) + ]; + } + + // Start share_chats backwards compatibility + if(!isset($config->CHATS_SHARE)) { + // Add buttons for predefined sharing chats. + // Default SHARE_CHATS or special chat list via $chats? + if(empty($chats)) { + if(!empty($raidLevel)) { + // find chats to share ourselves, if we can + debug_log($raidLevel, 'Did not get specific chats to share to, checking level specific for: '); + $level_chat = 'SHARE_CHATS_LEVEL_' . $raidLevel; + $chats = []; + if(!empty($config->{$level_chat})) { + $chat_array = explode(',', $config->{$level_chat}); + debug_log($config->{$level_chat}, 'Found level specific chats to share to: '); + } else { + $chat_array = explode(',', $config->SHARE_CHATS); + debug_log($config->SHARE_CHATS, 'Chats not specified for level, sharing to globals: '); + } + } else { + $chat_array = explode(',', $config->SHARE_CHATS); + debug_log($config->SHARE_CHATS, 'Level not given, sharing to globals: '); + } + foreach($chat_array as $chat) { + $chats[] = ['id' => $chat]; + } + } + // End chats_share backwards compatibility + } else { + if(empty($chats)) { + if(!empty($raidLevel)) { + // find chats to share ourselves, if we can + debug_log($raidLevel, 'Did not get specific chats to share to, checking level specific for: '); + $chats = []; + if(!empty($config->CHATS_SHARE['manual_share'][$raidLevel])) { + $chats = $config->CHATS_SHARE['manual_share'][$raidLevel]; + } else { + $chats = $config->CHATS_SHARE['manual_share']['all']; + } + } else { + $chats = $config->CHATS_SHARE['manual_share']['all']; + } + } + } + // Add keys for each chat. + if(empty($chats)) { + debug_log('Aint got any chats to share to!'); + return []; + } + if($raidLevel == '') { + // If raid level is not set we are sharing something else than a raid. + $sharedChats = []; + } else { + $queryShared = my_query(' + SELECT DISTINCT chat_id + FROM cleanup + WHERE raid_id = ?', + [$id] + ); + $sharedChats = $queryShared->fetchAll(PDO::FETCH_COLUMN, 0); + } + foreach($chats as $chat) { + if(in_array($chat['id'], $sharedChats)) continue; + // Get chat object + debug_log("Getting chat object for '" . $chat['id'] . "'"); + $chat_obj = get_chat($chat['id']); + + // Check chat object for proper response. + if ($chat_obj['ok'] != true) { + info_log($chat, 'Invalid chat id in your configuration:'); + continue; + } + $chatTitle = $chat['title'] ?? $chat_obj['result']['title']; + debug_log('Proper chat object received, continuing to add key for this chat: ' . $chatTitle); + $shareData = [0 => $action, 'c' => $chat['id']]; + $shareData['t'] = $chat['thread'] ?? ''; + if($id !== false) $shareData['r'] = $id; + $keys[][] = button(getTranslation('share_with') . ' ' . $chatTitle, $shareData); + } + + return $keys; +} + +/** + * Format + * @param array $array + * @return string Formated + */ +function formatCallbackData($array) +{ + $return = $array[0] . '|'; + unset($array[0]); + foreach($array as $key => $value) { + if($value !== 0 && (!isset($value) or $value === false)) continue; + $return .= $key . '=' . $value . '|'; + } + return rtrim($return, '|'); +} + +/** + * Return a button element + * @param string Button text + * @param string|array Callback data + * @return array Button + */ +function button($text, $callbackData) +{ + $callback = is_array($callbackData) ? formatCallbackData($callbackData) : $callbackData; + $button = [ + 'text' => $text, + 'callback_data' => $callback + ]; + return $button; +} diff --git a/logic/keys_event.php b/logic/keys_event.php index 388cb409..96278cac 100644 --- a/logic/keys_event.php +++ b/logic/keys_event.php @@ -1,34 +1,36 @@ fetch()) { - if(!empty($event['name'])) { - $keys[] = array( - 'text' => $event['name'], - 'callback_data' => $gym_id_plus_letter . ':' . $action . ':' . $event['id'] - ); - }else { - info_log('Invalid event name on event '. $event['id']); - } + if(empty($event['name'])) { + info_log('Invalid event name on event '. $event['id']); + continue; + } + $callbackData['e'] = $event['id']; + $keys[] = button($event['name'], $callbackData); } - $keys[] = array( - 'text' => getTranslation("Xstars"), - 'callback_data' => $gym_id_plus_letter . ':' . $action . ':X' - ); - // Get the inline key array. - $keys = inline_key_array($keys, 1); + } + if($admin_access[0] === true) { + $callbackData['e'] = EVENT_ID_EX; + $keys[] = button(getTranslation(RAID_ID_EX . 'stars'), $callbackData); + } + // Get the inline key array. + $keys = inline_key_array($keys, 1); - return $keys; + return $keys; } -?> \ No newline at end of file diff --git a/logic/keys_trainerinfo.php b/logic/keys_trainerinfo.php index aea1d9c6..4bf93c11 100644 --- a/logic/keys_trainerinfo.php +++ b/logic/keys_trainerinfo.php @@ -6,61 +6,26 @@ */ function keys_trainerinfo($show = false) { - global $config; - // Toggle state. - $status = 'show'; - if($show || !$config->TRAINER_BUTTONS_TOGGLE) { - // Always show buttons? - if(($show == true && !$config->TRAINER_BUTTONS_TOGGLE) || $config->TRAINER_BUTTONS_TOGGLE) { - $status = 'hide'; - } + global $config; + // Toggle state. + $status = 'show'; + if(!$show) { + // Key to show/hide trainer info. + $keys[][] = button(getPublicTranslation('trainerinfo'), ['vote_level', 'a' => 'trainer', 's' => $status]); + return $keys; + } + // Always show buttons? + if($show == true) { + $status = 'hide'; + } - // Keys to set team and level - $keys = [ - [ - [ - 'text' => getPublicTranslation('trainerinfo'), - 'callback_data' => 'trainer:vote_level:' . $status - ], - ], - [ - [ - 'text' => getPublicTranslation('team') . SP . TEAM_B, - 'callback_data' => 'trainer:vote_team:mystic' - ], - [ - 'text' => getPublicTranslation('team') . SP . TEAM_R, - 'callback_data' => 'trainer:vote_team:valor' - ], - [ - 'text' => getPublicTranslation('team') . SP . TEAM_Y, - 'callback_data' => 'trainer:vote_team:instinct' - ], - ], - [ - [ - 'text' => getPublicTranslation('level') . ' +', - 'callback_data' => 'trainer:vote_level:up' - ], - [ - 'text' => getPublicTranslation('level') . ' -', - 'callback_data' => 'trainer:vote_level:down' - ] - ] - ]; - } else { - // Key to show/hide trainer info. - $keys = [ - [ - [ - 'text' => getPublicTranslation('trainerinfo'), - 'callback_data' => 'trainer:vote_level:' . $status - ], - ] - ]; - } + // Keys to set team and level + $keys[0][0] = button(getPublicTranslation('trainerinfo'), ['vote_level', 'a' => 'trainer', 's' => $status]); + $keys[1][0] = button(getPublicTranslation('team') . SP . TEAM_B, ['vote_team', 'a' => 'trainer', 't' => 'mystic']); + $keys[1][1] = button(getPublicTranslation('team') . SP . TEAM_R, ['vote_team', 'a' => 'trainer', 't' => 'valor']); + $keys[1][2] = button(getPublicTranslation('team') . SP . TEAM_Y, ['vote_team', 'a' => 'trainer', 't' => 'instinct']); + $keys[2][0] = button(getPublicTranslation('level') . ' +', ['vote_level', 'a' => 'trainer', 'l' => 'up']); + $keys[2][1] = button(getPublicTranslation('level') . ' -', ['vote_level', 'a' => 'trainer', 'l' => 'down']); - return $keys; + return $keys; } - -?> diff --git a/logic/keys_vote.php b/logic/keys_vote.php index 6d9baf1b..95adc12c 100644 --- a/logic/keys_vote.php +++ b/logic/keys_vote.php @@ -1,4 +1,5 @@ RAID_ENDED_HIDE_KEYS) { - $keys = []; - }else { - $keys = [ - [ - [ - 'text' => getPublicTranslation('raid_done'), - 'callback_data' => $raid['id'] . ':vote_refresh:1' - ] - ] - ]; - } - // Raid is still running. + global $config; + // Init keys_time array. + $keys_time = []; + + // Get current UTC time and raid UTC times. + $now = utcnow(); + + // Write to log. + debug_log($now, 'UTC NOW:'); + debug_log($raid['end_time'], 'UTC END:'); + debug_log($raid['start_time'], 'UTC START:'); + + // Raid ended already. + if ($raid['raid_ended']) { + if($config->RAID_ENDED_HIDE_KEYS) return []; + $button[][] = button(getPublicTranslation('raid_done'), ['vote_refresh', 'r' => $raid['id']]); + return $button; + } + // Raid is still running. + // Get current pokemon + $raid_pokemon_id = $raid['pokemon']; + $raid_pokemon_form_id = $raid['pokemon_form']; + $raid_pokemon = $raid_pokemon_id . "-" . $raid_pokemon_form_id; + + // Get raid level + $raid_level = $raid['level']; + + // Are remote players allowed for this raid? + $raid_local_only = in_array($raid_level, RAID_LEVEL_LOCAL_ONLY); + + // Hide buttons for raid levels and pokemon + $hide_buttons_raid_level = explode(',', $config->RAID_POLL_HIDE_BUTTONS_RAID_LEVEL); + $hide_buttons_pokemon = explode(',', $config->RAID_POLL_HIDE_BUTTONS_POKEMON); + + // Show buttons to users? + if(in_array($raid_level, $hide_buttons_raid_level) || in_array(($raid_pokemon_id . "-" . get_pokemon_form_name($raid_pokemon_id,$raid_pokemon_form_id)), $hide_buttons_pokemon) || in_array($raid_pokemon_id, $hide_buttons_pokemon)) { + return []; + } + // Extra Keys + $buttons['alone'] = button(EMOJI_SINGLE, ['vote_extra', 'r' => $raid['id']]); + $buttons['extra'] = button('+ ' . EMOJI_IN_PERSON, ['vote_extra', 'r' => $raid['id'], 'a' => 'in_person']); + + // Show buttons regarding remote participation only if raid level allows it + $buttons['extra_alien'] = $buttons['can_inv'] = $buttons['remote'] = $buttons['inv_plz'] = []; + if(!$raid_local_only) { + $buttons['extra_alien'] = button('+ ' . EMOJI_ALIEN, ['vote_extra', 'r' => $raid['id'], 'a' => 'alien']); + + // Can invite key + $buttons['can_inv'] = button(EMOJI_CAN_INVITE, ['vote_can_invite', 'r' => $raid['id']]); + + // Remote Raid Pass key + $buttons['remote'] = button(EMOJI_REMOTE, ['vote_remote', 'r' => $raid['id']]); + + // Want invite key + $buttons['inv_plz'] = button(EMOJI_WANT_INVITE, ['vote_want_invite', 'r' => $raid['id']]); + } + + // Team and level keys. + $buttons['teamlvl'][0][] = button('Team', ['vote_team', 'r' => $raid['id']]); + $buttons['teamlvl'][0][] = button('Lvl +', ['vote_level', 'r' => $raid['id'], 'l' => 'up']); + $buttons['teamlvl'][0][] = button('Lvl -', ['vote_level', 'r' => $raid['id'], 'l' => 'down']); + + // Ex-Raid Invite key + $buttons['ex_inv'] = []; + if ($raid['event'] == EVENT_ID_EX) { + $buttons['ex_inv'] = button(EMOJI_INVITE, ['vote_invite', 'r' => $raid['id']]); + } + + // Show icon, icon + text or just text. + // Icon. + if($config->RAID_VOTE_ICONS && !$config->RAID_VOTE_TEXT) { + $text_here = EMOJI_HERE; + $text_late = EMOJI_LATE; + $text_done = TEAM_DONE; + $text_cancel = TEAM_CANCEL; + // Icon + text. + } else if($config->RAID_VOTE_ICONS && $config->RAID_VOTE_TEXT) { + $text_here = EMOJI_HERE . getPublicTranslation('here'); + $text_late = EMOJI_LATE . getPublicTranslation('late'); + $text_done = TEAM_DONE . getPublicTranslation('done'); + $text_cancel = TEAM_CANCEL . getPublicTranslation('cancellation'); + // Text. + } else { + $text_here = getPublicTranslation('here'); + $text_late = getPublicTranslation('late'); + $text_done = getPublicTranslation('done'); + $text_cancel = getPublicTranslation('cancellation'); + } + + // Status keys. + $buttons['alarm'] = button(EMOJI_ALARM, ['vote_status', 'r' => $raid['id'], 'a' => 'alarm']); + $buttons['here'] = button($text_here, ['vote_status', 'r' => $raid['id'], 'a' => 'arrived']); + $buttons['late'] = button($text_late, ['vote_status', 'r' => $raid['id'], 'a' => 'late']); + $buttons['done'] = button($text_done, ['vote_status', 'r' => $raid['id'], 'a' => 'raid_done']); + $buttons['cancel'] = button($text_cancel, ['vote_status', 'r' => $raid['id'], 'a' => 'cancel']); + + $buttons['refresh'] = []; + if(!$config->AUTO_REFRESH_POLLS) { + $buttons['refresh'] = button(EMOJI_REFRESH, ['vote_refresh', 'r' => $raid['id']]); + } + + if($raid['event_vote_key_mode'] == 1) { + $keys_time[] = button(getPublicTranslation('Participate'), ['vote_time', 'r' => $raid['id'], 't' => utctime($raid['start_time'], 'YmdHis')]); + }else { + $RAID_SLOTS = ($raid['event_time_slots'] > 0) ? $raid['event_time_slots'] : $config->RAID_SLOTS; + $keys_time = generateTimeslotKeys($RAID_SLOTS, $raid); + } + // Add time keys. + $buttons['time'] = inline_key_array($keys_time, 4); + + // Hidden participants? + $hide_users_sql = ''; + if($config->RAID_POLL_HIDE_USERS_TIME > 0) { + if($config->RAID_ANYTIME) { + $hide_users_sql = 'AND (attend_time > (UTC_TIMESTAMP() - INTERVAL ' . $config->RAID_POLL_HIDE_USERS_TIME . ' MINUTE) OR attend_time = \''.ANYTIME.'\')'; } else { - // Get current pokemon - $raid_pokemon_id = $raid['pokemon']; - $raid_pokemon_form_id = $raid['pokemon_form']; - $raid_pokemon = $raid_pokemon_id . "-" . $raid_pokemon_form_id; - - // Get raid level - $raid_level = $raid['level']; - - // Hide buttons for raid levels and pokemon - $hide_buttons_raid_level = explode(',', $config->RAID_POLL_HIDE_BUTTONS_RAID_LEVEL); - $hide_buttons_pokemon = explode(',', $config->RAID_POLL_HIDE_BUTTONS_POKEMON); - - // Show buttons to users? - if(in_array($raid_level, $hide_buttons_raid_level) || in_array(($raid_pokemon_id . "-" . get_pokemon_form_name($raid_pokemon_id,$raid_pokemon_form_id)), $hide_buttons_pokemon) || in_array($raid_pokemon_id, $hide_buttons_pokemon)) { - $keys = []; - } else { - // Extra Keys - $buttons_alone = [ - 'text' => EMOJI_SINGLE, - 'callback_data' => $raid['id'] . ':vote_extra:0' - ]; - $buttons_extra = [ - 'text' => '+ ' . EMOJI_IN_PERSON, - 'callback_data' => $raid['id'] . ':vote_extra:in_person' - ]; - $buttons_extra_alien = [ - 'text' => '+ ' . EMOJI_ALIEN, - 'callback_data' => $raid['id'] . ':vote_extra:alien' - ]; - - // Can invite key - $buttons_can_inv = [ - 'text' => EMOJI_CAN_INVITE, - 'callback_data' => $raid['id'] . ':vote_can_invite:0' - ]; - - // Remote Raid Pass key - $buttons_remote = [ - 'text' => EMOJI_REMOTE, - 'callback_data' => $raid['id'] . ':vote_remote:0' - ]; - - // Want invite key - $buttons_inv_plz = [ - 'text' => EMOJI_WANT_INVITE, - 'callback_data' => $raid['id'] . ':vote_want_invite:0' - ]; - - // Team and level keys. - $buttons_teamlvl = [ - [ - [ - 'text' => 'Team', - 'callback_data' => $raid['id'] . ':vote_team:0' - ], - [ - 'text' => 'Lvl +', - 'callback_data' => $raid['id'] . ':vote_level:up' - ], - [ - 'text' => 'Lvl -', - 'callback_data' => $raid['id'] . ':vote_level:down' - ] - ] - ]; - - // Ex-Raid Invite key - if ($raid['event'] == EVENT_ID_EX) { - $buttons_ex_inv = [ - 'text' => EMOJI_INVITE, - 'callback_data' => $raid['id'] . ':vote_invite:0' - ]; - }else { - $buttons_ex_inv = []; - } - - // Show icon, icon + text or just text. - // Icon. - if($config->RAID_VOTE_ICONS && !$config->RAID_VOTE_TEXT) { - $text_here = EMOJI_HERE; - $text_late = EMOJI_LATE; - $text_done = TEAM_DONE; - $text_cancel = TEAM_CANCEL; - // Icon + text. - } else if($config->RAID_VOTE_ICONS && $config->RAID_VOTE_TEXT) { - $text_here = EMOJI_HERE . getPublicTranslation('here'); - $text_late = EMOJI_LATE . getPublicTranslation('late'); - $text_done = TEAM_DONE . getPublicTranslation('done'); - $text_cancel = TEAM_CANCEL . getPublicTranslation('cancellation'); - // Text. - } else { - $text_here = getPublicTranslation('here'); - $text_late = getPublicTranslation('late'); - $text_done = getPublicTranslation('done'); - $text_cancel = getPublicTranslation('cancellation'); - } - - // Status keys. - $buttons_alarm = [ - 'text' => EMOJI_ALARM, - 'callback_data' => $raid['id'] . ':vote_status:alarm' - ]; - $buttons_here = [ - 'text' => $text_here, - 'callback_data' => $raid['id'] . ':vote_status:arrived' - ]; - $buttons_late = [ - 'text' => $text_late, - 'callback_data' => $raid['id'] . ':vote_status:late' - ]; - $buttons_done = [ - 'text' => $text_done, - 'callback_data' => $raid['id'] . ':vote_status:raid_done' - ]; - $buttons_cancel = [ - 'text' => $text_cancel, - 'callback_data' => $raid['id'] . ':vote_status:cancel' - ]; - - if(!$config->AUTO_REFRESH_POLLS) { - $buttons_refresh = [ - 'text' => EMOJI_REFRESH, - 'callback_data' => $raid['id'] . ':vote_refresh:0' - ]; - }else { - $buttons_refresh = []; - } - - if($raid['event_vote_key_mode'] == 1) { - $keys_time = [ - [ - 'text' => getPublicTranslation("Participate"), - 'callback_data' => $raid['id'] . ':vote_time:0' - ] - ]; - }else { - if($raid['event_time_slots'] > 0) { - $RAID_SLOTS = $raid['event_time_slots']; - }else { - $RAID_SLOTS = $config->RAID_SLOTS; - } - // Get current time. - $now_helper = new DateTimeImmutable('now', new DateTimeZone('UTC')); - $now_helper = $now_helper->format('Y-m-d H:i') . ':00'; - $dt_now = new DateTimeImmutable($now_helper, new DateTimeZone('UTC')); - - // Get direct start slot - $direct_slot = new DateTimeImmutable($start_time, new DateTimeZone('UTC')); - - // Get first raidslot rounded up to the next 5 minutes - // Get minute and convert modulo raidslot - $five_slot = new DateTimeImmutable($start_time, new DateTimeZone('UTC')); - $minute = $five_slot->format("i"); - $minute = $minute % 5; - - // Count minutes to next 5 multiple minutes if necessary - if($minute != 0) - { - // Count difference - $diff = 5 - $minute; - // Add difference - $five_slot = $five_slot->add(new DateInterval("PT".$diff."M")); - } - - // Add $config->RAID_FIRST_START minutes to five minutes slot - //$five_plus_slot = new DateTime($five_slot, new DateTimeZone('UTC')); - $five_plus_slot = $five_slot; - $five_plus_slot = $five_plus_slot->add(new DateInterval("PT".$config->RAID_FIRST_START."M")); - - // Get first regular raidslot - // Get minute and convert modulo raidslot - $first_slot = new DateTimeImmutable($start_time, new DateTimeZone('UTC')); - $minute = $first_slot->format("i"); - $minute = $minute % $config->RAID_SLOTS; - - // Count minutes to next raidslot multiple minutes if necessary - if($minute != 0) - { - // Count difference - $diff = $config->RAID_SLOTS - $minute; - // Add difference - $first_slot = $first_slot->add(new DateInterval("PT".$diff."M")); - } - - // Compare times slots to add them to keys. - // Example Scenarios: - // Raid 1: Start = 17:45, $config->RAID_FIRST_START = 10, $config->RAID_SLOTS = 15 - // Raid 2: Start = 17:36, $config->RAID_FIRST_START = 10, $config->RAID_SLOTS = 15 - // Raid 3: Start = 17:35, $config->RAID_FIRST_START = 10, $config->RAID_SLOTS = 15 - // Raid 4: Start = 17:31, $config->RAID_FIRST_START = 10, $config->RAID_SLOTS = 15 - // Raid 5: Start = 17:40, $config->RAID_FIRST_START = 10, $config->RAID_SLOTS = 15 - // Raid 6: Start = 17:32, $config->RAID_FIRST_START = 5, $config->RAID_SLOTS = 5 - - // Write slots to log. - debug_log($direct_slot, 'Direct start slot:'); - debug_log($five_slot, 'Next 5 Minute slot:'); - debug_log($first_slot, 'First regular slot:'); - - // Add first slot only, as all slot times are identical - if($direct_slot == $five_slot && $direct_slot == $first_slot) { - // Raid 1: 17:45 (17:45 == 17:45 && 17:45 == 17:45) - - // Add first slot - if($first_slot >= $dt_now) { - $slot = $first_slot->format('Y-m-d H:i:s'); - $keys_time[] = array( - 'text' => dt2time($slot), - 'callback_data' => $raid['id'] . ':vote_time:' . utctime($slot, 'YmdHis') - ); - } - - // Add either five and first slot or only first slot based on RAID_FIRST_START - } else if($direct_slot == $five_slot && $five_slot < $first_slot) { - // Raid 3: 17:35 == 17:35 && 17:35 < 17:45 - // Raid 5: 17:40 == 17:40 && 17:40 < 17:45 - - // Add next five minutes slot and first regular slot - if($five_plus_slot <= $first_slot) { - // Raid 3: 17:35, 17:45 (17:35 + 10min <= 17:45) - - // Add five minutes slot - if($five_slot >= $dt_now) { - $slot = $five_slot->format('Y-m-d H:i:s'); - $keys_time[] = array( - 'text' => dt2time($slot), - 'callback_data' => $raid['id'] . ':vote_time:' . utctime($slot, 'YmdHis') - ); - } - - // Add first slot - if($first_slot >= $dt_now) { - $slot = $first_slot->format('Y-m-d H:i:s'); - $keys_time[] = array( - 'text' => dt2time($slot), - 'callback_data' => $raid['id'] . ':vote_time:' . utctime($slot, 'YmdHis') - ); - } - - // Add only first regular slot - } else { - // Raid 5: 17:45 - - // Add first slot - if($first_slot >= $dt_now) { - $slot = $first_slot->format('Y-m-d H:i:s'); - $keys_time[] = array( - 'text' => dt2time($slot), - 'callback_data' => $raid['id'] . ':vote_time:' . utctime($slot, 'YmdHis') - ); - } - } - - // Add direct slot and first slot - } else if($direct_slot < $five_slot && $five_slot == $first_slot) { - // Raid 6: 17:32 < 17:35 && 17:35 == 17:35 - // Some kind of special case for a low value of RAID_SLOTS - - // Add direct slot? - if($config->RAID_DIRECT_START) { - if($direct_slot >= $dt_now) { - $slot = $direct_slot->format('Y-m-d H:i:s'); - $keys_time[] = array( - 'text' => dt2time($slot), - 'callback_data' => $raid['id'] . ':vote_time:' . utctime($slot, 'YmdHis') - ); - } - } - - // Add first slot - if($first_slot >= $dt_now) { - $slot = $first_slot->format('Y-m-d H:i:s'); - $keys_time[] = array( - 'text' => dt2time($slot), - 'callback_data' => $raid['id'] . ':vote_time:' . utctime($slot, 'YmdHis') - ); - } - - - // Add either all 3 slots (direct slot, five minutes slot and first regular slot) or - // 2 slots (direct slot and first slot) as $config->RAID_FIRST_START does not allow the five minutes slot to be added - } else if($direct_slot < $five_slot && $five_slot < $first_slot) { - // Raid 2: 17:36 < 17:40 && 17:40 < 17:45 - // Raid 4: 17:31 < 17:35 && 17:35 < 17:45 - - // Add all 3 slots - if($five_plus_slot <= $first_slot) { - // Raid 4: 17:31, 17:35, 17:45 - - // Add direct slot? - if($config->RAID_DIRECT_START) { - if($direct_slot >= $dt_now) { - $slot = $direct_slot->format('Y-m-d H:i:s'); - $keys_time[] = array( - 'text' => dt2time($slot), - 'callback_data' => $raid['id'] . ':vote_time:' . utctime($slot, 'YmdHis') - ); - } - } - - // Add five minutes slot - if($five_slot >= $dt_now) { - $slot = $five_slot->format('Y-m-d H:i:s'); - $keys_time[] = array( - 'text' => dt2time($slot), - 'callback_data' => $raid['id'] . ':vote_time:' . utctime($slot, 'YmdHis') - ); - } - - // Add first slot - if($first_slot >= $dt_now) { - $slot = $first_slot->format('Y-m-d H:i:s'); - $keys_time[] = array( - 'text' => dt2time($slot), - 'callback_data' => $raid['id'] . ':vote_time:' . utctime($slot, 'YmdHis') - ); - } - // Add direct slot and first regular slot - } else { - // Raid 2: 17:36, 17:45 - - // Add direct slot? - if($config->RAID_DIRECT_START) { - if($direct_slot >= $dt_now) { - $slot = $direct_slot->format('Y-m-d H:i:s'); - $keys_time[] = array( - 'text' => dt2time($slot), - 'callback_data' => $raid['id'] . ':vote_time:' . utctime($slot, 'YmdHis') - ); - } - } - - // Add first slot - if($first_slot >= $dt_now) { - $slot = $first_slot->format('Y-m-d H:i:s'); - $keys_time[] = array( - 'text' => dt2time($slot), - 'callback_data' => $raid['id'] . ':vote_time:' . utctime($slot, 'YmdHis') - ); - } - } - - // We missed all possible cases or forgot to include them in future else-if-clauses :D - // Try to add at least the direct slot. - } else { - // Add direct slot? - if($config->RAID_DIRECT_START) { - if($first_slot >= $dt_now) { - $slot = $direct_slot->format('Y-m-d H:i:s'); - $keys_time[] = array( - 'text' => dt2time($slot), - 'callback_data' => $raid['id'] . ':vote_time:' . utctime($slot, 'YmdHis') - ); - } - } - } - - - // Init last slot time. - $last_slot = new DateTimeImmutable($start_time, new DateTimeZone('UTC')); - - // Get regular slots - // Start with second slot as first slot is already added to keys. - $second_slot = $first_slot->add(new DateInterval("PT".$RAID_SLOTS."M")); - $dt_end = new DateTimeImmutable($end_time, new DateTimeZone('UTC')); - $regular_slots = new DatePeriod($second_slot, new DateInterval('PT'.$RAID_SLOTS.'M'), $dt_end); - - // Add regular slots. - foreach($regular_slots as $slot){ - $slot_end = $slot->add(new DateInterval('PT'.$config->RAID_LAST_START.'M')); - // Slot + $config->RAID_LAST_START before end_time? - if($slot_end < $dt_end) { - debug_log($slot, 'Regular slot:'); - // Add regular slot. - if($slot >= $dt_now) { - $slot = $slot->format('Y-m-d H:i:s'); - $keys_time[] = array( - 'text' => dt2time($slot), - 'callback_data' => $raid['id'] . ':vote_time:' . utctime($slot, 'YmdHis') - ); - - // Set last slot for later. - $last_slot = new DateTimeImmutable($slot, new DateTimeZone('UTC')); - } else { - // Set last slot for later. - $slot = $slot->format('Y-m-d H:i:s'); - $last_slot = new DateTimeImmutable($slot, new DateTimeZone('UTC')); - } - } - } - - // Add raid last start slot - // Set end_time to last extra slot, subtract $config->RAID_LAST_START minutes and round down to earlier 5 minutes. - $last_extra_slot = $dt_end; - $last_extra_slot = $last_extra_slot->sub(new DateInterval('PT'.$config->RAID_LAST_START.'M')); - $s = 5 * 60; - $last_extra_slot = $last_extra_slot->setTimestamp($s * floor($last_extra_slot->getTimestamp() / $s)); - //$time_to_last_slot = $last_extra_slot->diff($last_slot)->format("%a"); - - // Last extra slot not conflicting with last slot and time to last regular slot larger than RAID_LAST_START? - //if($last_extra_slot > $last_slot && $time_to_last_slot > $config->RAID_LAST_START) - - // Log last and last extra slot. - debug_log($last_slot, 'Last slot:'); - debug_log($last_extra_slot, 'Last extra slot:'); - - // Last extra slot not conflicting with last slot - if($last_extra_slot > $last_slot) { - // Add last extra slot - if($last_extra_slot >= $dt_now) { - $slot = $last_extra_slot->format('Y-m-d H:i:s'); - $keys_time[] = array( - 'text' => dt2time($slot), - 'callback_data' => $raid['id'] . ':vote_time:' . utctime($slot, 'YmdHis') - ); - } - } - - // Attend raid at any time - if($config->RAID_ANYTIME) - { - $keys_time[] = array( - 'text' => getPublicTranslation('anytime'), - 'callback_data' => $raid['id'] . ':vote_time:0' - ); - } - } - // Add time keys. - $buttons_time = inline_key_array($keys_time, 4); - - // Hidden participants? - if($config->RAID_POLL_HIDE_USERS_TIME > 0) { - if($config->RAID_ANYTIME) { - $hide_users_sql = "AND (attend_time > (UTC_TIMESTAMP() - INTERVAL " . $config->RAID_POLL_HIDE_USERS_TIME . " MINUTE) OR attend_time = '".ANYTIME."')"; - } else { - $hide_users_sql = "AND attend_time > (UTC_TIMESTAMP() - INTERVAL " . $config->RAID_POLL_HIDE_USERS_TIME . " MINUTE)"; - } - } else { - $hide_users_sql = ""; - } - - // Get participants - $rs = my_query( - " - SELECT count(attend_time) AS count, - sum(pokemon = '0') AS count_any_pokemon, - sum(pokemon = '{$raid_pokemon}') AS count_raid_pokemon - FROM attendance - WHERE raid_id = {$raid['id']} - $hide_users_sql - AND attend_time IS NOT NULL - AND raid_done != 1 - AND cancel != 1 - " - ); - - $row = $rs->fetch(); - - // Count participants and participants by pokemon - $count_pp = $row['count']; - $count_any_pokemon = $row['count_any_pokemon']; - $count_raid_pokemon = $row['count_raid_pokemon']; - - // Write to log. - debug_log('Participants for raid with ID ' . $raid['id'] . ': ' . $count_pp); - debug_log('Participants who voted for any pokemon: ' . $count_any_pokemon); - debug_log('Participants who voted for ' . $raid_pokemon . ': ' . $count_raid_pokemon); - - // Zero Participants? Show only time buttons! - if($count_pp == 0) { - $keys = $buttons_time; - } else { - // Init keys pokemon array. - $buttons_pokemon = []; - - // Show pokemon keys only if the raid boss is an egg - if(in_array($raid_pokemon_id, $GLOBALS['eggs'])) { - // Get pokemon from database - $raid_spawn = dt2time($raid['spawn'], 'Y-m-d H:i'); // Convert utc spawntime to local time - $raid_bosses = get_raid_bosses($raid_spawn, $raid_level); - - // Get eggs. - $eggs = $GLOBALS['eggs']; - - if(count($raid_bosses) > 2) { - // Add key for each raid level - foreach($raid_bosses as $pokemon) { - if(in_array($pokemon['pokedex_id'], $eggs)) continue; - $buttons_pokemon[] = array( - 'text' => get_local_pokemon_name($pokemon['pokedex_id'], $pokemon['pokemon_form_id'], true), - 'callback_data' => $raid['id'] . ':vote_pokemon:' . $pokemon['pokedex_id'] . '-' . $pokemon['pokemon_form_id'] - ); - } - - // Add button if raid boss does not matter - $buttons_pokemon[] = array( - 'text' => getPublicTranslation('any_pokemon'), - 'callback_data' => $raid['id'] . ':vote_pokemon:0' - ); - - // Finally add pokemon to keys - $buttons_pokemon = inline_key_array($buttons_pokemon, 2); - } - } - - // Init keys array - $keys = []; - - if($raid['event_poll_template'] != null) $template = json_decode($raid['event_poll_template']); - else $template = $config->RAID_POLL_UI_TEMPLATE; - $r=0; - foreach($template as $row) { - foreach($row as $key) { - $v_name = 'buttons_'.$key; - if($key == 'teamlvl' or $key == 'pokemon' or $key == 'time') { - // Some button variables are "blocks" of keys, process them here - if(empty(${$v_name})) continue; - foreach(${$v_name} as $teamlvl) { - if(!isset($keys[$r])) $keys[$r] = []; - $keys[$r] = array_merge($keys[$r],$teamlvl); - $r++; - } - $r--; - }else { - if(empty(${$v_name})) continue; - $keys[$r][] = ${$v_name}; - } - } - if(!empty($keys[$r][0])) $r++; - } - } + $hide_users_sql = 'AND attend_time > (UTC_TIMESTAMP() - INTERVAL ' . $config->RAID_POLL_HIDE_USERS_TIME . ' MINUTE)'; + } + } + + // Get participants + $rs = my_query(' + SELECT count(attend_time) AS count, + sum(pokemon = 0) AS count_any_pokemon, + sum(pokemon = ?) AS count_raid_pokemon + FROM attendance + WHERE raid_id = ? + ' . $hide_users_sql . ' + AND attend_time IS NOT NULL + AND raid_done != 1 + AND cancel != 1 + ', [$raid_pokemon, $raid['id']] + ); + + $row = $rs->fetch(); + + // Count participants and participants by pokemon + $count_pp = $row['count']; + $count_any_pokemon = $row['count_any_pokemon']; + $count_raid_pokemon = $row['count_raid_pokemon']; + + // Write to log. + debug_log('Participants for raid with ID ' . $raid['id'] . ': ' . $count_pp); + debug_log('Participants who voted for any pokemon: ' . $count_any_pokemon); + debug_log('Participants who voted for ' . $raid_pokemon . ': ' . $count_raid_pokemon); + + // Zero Participants? Show only time buttons! + if($row['count'] == 0) { + return $buttons['time']; + } + + // Init keys pokemon array. + $buttons['pokemon'] = []; + // Show pokemon keys only if the raid boss is an egg + if(in_array($raid_pokemon_id, EGGS)) { + // Get pokemon from database + $raid_spawn = dt2time($raid['spawn'], 'Y-m-d H:i'); // Convert utc spawntime to local time + $raid_bosses = get_raid_bosses($raid_spawn, $raid_level); + + if(count($raid_bosses) > 2) { + // Add key for each raid level + foreach($raid_bosses as $pokemon) { + if(in_array($pokemon['pokedex_id'], EGGS)) continue; + $buttons['pokemon'][] = button( + get_local_pokemon_name($pokemon['pokedex_id'], $pokemon['pokemon_form_id'], $config->LANGUAGE_PUBLIC), + ['vote_pokemon', 'r' => $raid['id'], 'p' => $pokemon['pokedex_id'] . '-' . $pokemon['pokemon_form_id']] + ); + } + + // Add button if raid boss does not matter + $buttons['pokemon'][] = button(getPublicTranslation('any_pokemon'), ['vote_pokemon', 'r' => $raid['id']]); + + // Finally add pokemon to keys + $buttons['pokemon'] = inline_key_array($buttons['pokemon'], 2); + } + } + + // Init keys array + $keys = []; + + $template = $config->RAID_POLL_UI_TEMPLATE; + if($raid['event_poll_template'] != null) $template = json_decode($raid['event_poll_template']); + $r = 0; + foreach($template as $row) { + foreach($row as $key) { + if(!isset($buttons[$key]) or empty($buttons[$key])) continue; + if($key == 'teamlvl' or $key == 'pokemon' or $key == 'time') { + // Some button variables are "blocks" of keys, process them here + foreach($buttons[$key] as $teamlvl) { + if(!isset($keys[$r])) $keys[$r] = []; + $keys[$r] = array_merge($keys[$r],$teamlvl); + $r++; } + $r--; + continue; + } + $keys[$r][] = $buttons[$key]; } + if(!empty($keys[$r][0])) $r++; + } + + // Return the keys. + return $keys; +} - // Return the keys. - return $keys; +/** + * Get active raid bosses at a certain time. + * @param string $time - string, datetime, local time + * @param int $raid_level + * @return array + */ +function get_raid_bosses($time, $raid_level) +{ + // Get raid level from database + $rs = my_query(' + SELECT DISTINCT pokedex_id, pokemon_form_id + FROM raid_bosses + WHERE ? BETWEEN date_start AND date_end + AND raid_level = ? + ', [$time, $raid_level]); + debug_log('Checking active raid bosses for raid level '.$raid_level.' at '.$time.':'); + $raid_bosses = []; + $egg_found = false; + while ($result = $rs->fetch()) { + $raid_bosses[] = $result; + if($result['pokedex_id'] == '999'.$raid_level) $egg_found = true; + debug_log('Pokedex id: '.$result['pokedex_id'].' | Form id: '.$result['pokemon_form_id']); + } + if(!$egg_found) $raid_bosses[] = ['pokedex_id' => '999'.$raid_level, 'pokemon_form_id' => 0]; // Add egg if it wasn't found from db + return $raid_bosses; } -?> +/** + * Get active raid bosses at a certain time. + * @param int $RAID_SLOTS Length of the timeslot + * @param array $raid + * @return array + */ +function generateTimeslotKeys($RAID_SLOTS, $raid) { + global $config; + // Get current time. + $dt_now = DateTimeImmutable::createFromFormat('Y-m-d H:i', date('Y-m-d H:i')); + + // Get direct start slot + $direct_slot = new DateTimeImmutable($raid['start_time'], new DateTimeZone('UTC')); + $directStartMinutes = $direct_slot->format('i'); + + // Get first raidslot rounded up to the next 5 minutes + $minute = $directStartMinutes % 5; + $diff = ($minute != 0) ? 5 - $minute : 0; + $five_slot = $direct_slot->add(new DateInterval('PT'.$diff.'M')); + + // Add $config->RAID_FIRST_START minutes to five minutes slot + $five_plus_slot = $five_slot; + $five_plus_slot = $five_plus_slot->add(new DateInterval("PT".$config->RAID_FIRST_START."M")); + + // Get first regular raidslot + $firstSlotMinute = $directStartMinutes % $RAID_SLOTS; + $firstSlotDiff = ($firstSlotMinute != 0) ? $RAID_SLOTS - ($firstSlotMinute) : 0; + $first_slot = $direct_slot->add(new DateInterval('PT'.$firstSlotDiff.'M')); + + // Write slots to log. + debug_log($direct_slot, 'Direct start slot:'); + debug_log($five_slot, 'Next 5 Minute slot:'); + debug_log($first_slot, 'First regular slot:'); + $keys_time = []; + // Add button for direct start if needed + if(($config->RAID_DIRECT_START && !$config->RAID_RSVP_SLOTS) && $direct_slot != $five_slot && $direct_slot >= $dt_now) { + $keys_time[$direct_slot->format('YmdHi')] = button(timeslot_label($direct_slot), ['vote_time', 'r' => $raid['id'], 't' => $direct_slot->format('YmdHis')]); + } + + // Add button for first five minutes if needed + if($five_slot < $first_slot && $five_plus_slot <= $first_slot && $five_slot >= $dt_now) { + $keys_time[$five_slot->format('YmdHi')] = button(timeslot_label($five_slot), ['vote_time', 'r' => $raid['id'], 't' => $five_slot->format('YmdHis')]); + } + + // Get regular slots + // Start with second slot as first slot is already added to keys. + $dt_end = new DateTimeImmutable($raid['end_time'], new DateTimeZone('UTC')); + $regular_slots = new DatePeriod($first_slot, new DateInterval('PT'.$RAID_SLOTS.'M'), $dt_end->sub(new DateInterval('PT'.$config->RAID_LAST_START.'M'))); + + // Add regular slots. + foreach($regular_slots as $slot){ + debug_log($slot, 'Regular slot:'); + // Add regular slot. + if($slot >= $dt_now) { + $keys_time[$slot->format('YmdHi')] = button(timeslot_label($slot), ['vote_time', 'r' => $raid['id'], 't' => $slot->format('YmdHis')]); + } + // Set last slot for later. + $last_slot = $slot; + } + + // Add raid last start slot + // Set end_time to last extra slot, subtract $config->RAID_LAST_START minutes and round down to earlier 5 minutes. + $last_extra_slot = $dt_end->sub(new DateInterval('PT'.$config->RAID_LAST_START.'M')); + $s = 5 * 60; + $last_extra_slot = $last_extra_slot->setTimestamp($s * floor($last_extra_slot->getTimestamp() / $s)); + + // Log last and last extra slot. + if(isset($last_slot)) debug_log($last_slot, 'Last slot:'); + debug_log($last_extra_slot, 'Last extra slot:'); + + // Last extra slot not conflicting with last slot + if($last_extra_slot >= $dt_now && + ((isset($last_slot) && $last_extra_slot > $last_slot && $last_extra_slot != $last_slot) || + !isset($last_slot))) { + // Add last extra slot + $keys_time[$last_extra_slot->format('YmdHi')] = button(timeslot_label($last_extra_slot), ['vote_time', 'r' => $raid['id'], 't' => $last_extra_slot->format('YmdHis')]); + } + + if($config->RAID_RSVP_SLOTS) { + $rsvp_slots = new DatePeriod($direct_slot, new DateInterval('PT15M'), 2); + foreach($rsvp_slots as $slot){ + debug_log($slot, 'RSVP slot:'); + if($slot < $dt_now) continue; + // Add RSVP slot. + $keys_time[$slot->format('YmdHi')] = button(timeslot_label($slot), ['vote_time', 'r' => $raid['id'], 't' => $slot->format('YmdHis')]); + } + } + + // Sort keys by time and reindex array + asort($keys_time); + $keys_time = array_values($keys_time); + + // Attend raid at any time + if($config->RAID_ANYTIME) { + $keys_time[] = button(getPublicTranslation('anytime'), ['vote_time', 'r' => $raid['id']]); + } + return $keys_time; +} + +/** + * Generate timeslot label in local time. + * @param DateTime $datetime + * @return string + */ +function timeslot_label($datetime) +{ + global $config; + $tz = $config->TIMEZONE; + // Change the timezone of the object without changing it's time + $new = $datetime->setTimezone(new DateTimeZone($tz)); + return $new->format('H:i'); +} \ No newline at end of file diff --git a/logic/language.php b/logic/language.php new file mode 100644 index 00000000..3f3545da --- /dev/null +++ b/logic/language.php @@ -0,0 +1,105 @@ + false, + 'pokemonNames' => false, + 'pokemonMoves' => false, + 'pokemonForms' => false, + 'botHelp' => false, + 'custom' => false, + ]; + static $fileMap = [ + 'botLang' => 'language', + 'pokemonNames' => 'pokemon', + 'pokemonMoves' => 'pokemon_moves', + 'pokemonForms' => 'pokemon_forms', + 'botHelp' => 'help', + ]; + $translation = $savedTranslations[$translationTitle]; + + // Return translation if it's in memory already + if($translation !== false) return $translation; + + // Load translations from this file + $fileContents = file_get_contents(BOT_LANG_PATH . '/' . $fileMap[$translationTitle] . '.json'); + $translation = json_decode($fileContents, true); + + // Has custom translation already been processed? + if($savedTranslations['custom'] === false) { + $savedTranslations['custom'] = []; + // Load custom language file if it exists + if(is_file(CUSTOM_PATH . '/language.json')) { + $customContents = file_get_contents(CUSTOM_PATH . '/language.json'); + $savedTranslations['custom'] = json_decode($customContents, true); + } + } + + foreach($savedTranslations['custom'] as $title => $value) { + if(key_exists($title, $translation)) { + debug_log($title, 'Found custom translation for'); + // Only overwrite the translation for languages that are present + foreach($value as $lang => $newValue) { + $translation[$title][$lang] = $newValue; + } + unset($savedTranslations['custom'][$title]); + } + } + $savedTranslations[$translationTitle] = $translation; + + return $translation; +} + +/** + * Call the translation function with override parameters. + * @param string $text + * @return string translation + */ +function getPublicTranslation($text) +{ + global $config; + return getTranslation($text, $config->LANGUAGE_PUBLIC); +} + +/** + * Gets a table translation out of the json file. + * @param string $text + * @param string $override_language + * @return string translation + */ +function getTranslation($text, $language = false) +{ + global $botUser; + if($language === false) $language = $botUser->userLanguage; + $text = trim($text); + + $tfile = 'botLang'; + // Pokemon name? + if(strpos($text, 'pokemon_id_') === 0) $tfile = 'pokemonNames'; + + // Pokemon form? + if(strpos($text, 'pokemon_form_') === 0) $tfile = 'pokemonForms'; + + // Pokemon moves? + if(strpos($text, 'pokemon_move_') === 0) $tfile = 'pokemonMoves'; + + // Pokemon moves? + if(strpos($text, 'help_') === 0) $tfile = 'botHelp'; + + $translations = getTranslationFile($tfile); + + // Fallback to English when there is no language key or translation is not yet done. + if(isset($translations[$text][$language]) && $translations[$text][$language] != 'TRANSLATE') + $translation = $translations[$text][$language]; + elseif(isset($translations[$text][DEFAULT_LANGUAGE])) + $translation = $translations[$text][DEFAULT_LANGUAGE]; + + // No translation found + elseif($tfile == 'botHelp' or $tfile == 'pokemonForms') + $translation = false; + else + $translation = $text; + + debug_log("$text @ $tfile -> $translation", 'T:'); + return $translation; +} diff --git a/logic/mapslink.php b/logic/mapslink.php index cff5f41b..ad4ad728 100644 --- a/logic/mapslink.php +++ b/logic/mapslink.php @@ -21,7 +21,7 @@ function mapslink($gym, $gym_address = '0'){ switch ($gym_address) { case '1': //using gym address as maps link - $gym['address'] = 'https://maps.google.com/?daddr=' . $gym['lat'] . ',' . $gym['lon']; + $gym['address'] = 'https://maps.google.com/?daddr=' . $gym['lat'] . '%2C' . $gym['lon']; break; case '0': // do nothing -> getting default address from gym/raid @@ -33,13 +33,12 @@ function mapslink($gym, $gym_address = '0'){ if($maps_route){ // getting link for route calculation - $maps_link = '' . $gym['address'] . ''; + $maps_link = '' . $gym['address'] . ''; }else{ // getting link for normal maps point - $maps_link = '' . $gym['address'] . ''; + $maps_link = '' . $gym['address'] . ''; } // returning Maps Link return $maps_link; } -?> diff --git a/logic/new_user.php b/logic/new_user.php index b42867ab..f5e0f408 100644 --- a/logic/new_user.php +++ b/logic/new_user.php @@ -5,8 +5,23 @@ * @return bool */ function new_user($user_id) { - global $config; - if(!$config->TUTORIAL_MODE || user_tutorial($user_id) < $config->TUTORIAL_LEVEL_REQUIREMENT) return true; - else return false; + global $config, $botUser; + if($config->TUTORIAL_MODE && in_array("force-tutorial", $botUser->userPrivileges['privileges']) && user_tutorial($user_id) < $config->TUTORIAL_LEVEL_REQUIREMENT) + return true; + return false; +} + +/** + * Return the tutorial value from users table + * @param $user_id + * @return int + */ +function user_tutorial($user_id) { + debug_log('Reading user\'s tutorial value: '.$user_id); + $query = my_query('SELECT tutorial FROM users WHERE user_id = :user_id LIMIT 1', [":user_id"=>$user_id]); + $res = $query->fetch(); + $result = 0; + if($query->rowCount() > 0) $result = $res['tutorial']; + debug_log('Result: '.$result); + return $result; } -?> \ No newline at end of file diff --git a/logic/pokemon_keys.php b/logic/pokemon_keys.php index f282ef54..e18c5392 100644 --- a/logic/pokemon_keys.php +++ b/logic/pokemon_keys.php @@ -1,52 +1,50 @@ RAID_EGG_DURATION.' MINUTE) between date_start and date_end - OR DATE_ADD(\'' . $time_now . '\', INTERVAL '.$config->RAID_DURATION.' MINUTE) between date_start and date_end - ) - UNION - SELECT id, pokedex_id, pokemon_form_id - FROM pokemon - WHERE pokedex_id = \'' . $egg_id . '\' - ORDER BY pokedex_id - '; - $rs = my_query($query); - // Add key for each raid level - while ($pokemon = $rs->fetch()) { - $keys[] = array( - 'text' => get_local_pokemon_name($pokemon['pokedex_id'], $pokemon['pokemon_form_id']), - 'callback_data' => $gym_id_plus_letter . ':' . $action . ':' . (($event_id!==false) ? $event_id . ',' . $raid_level . ',' : '') . $pokemon['id'] - ); - } + // Get pokemon from database + $rs = my_query(' + SELECT pokemon.id, pokemon.pokedex_id, pokemon.pokemon_form_id + FROM raid_bosses + LEFT JOIN pokemon + ON pokemon.pokedex_id = raid_bosses.pokedex_id + AND pokemon.pokemon_form_id = raid_bosses.pokemon_form_id + WHERE raid_bosses.raid_level = :raidLevel + AND ( + DATE_SUB(\'' . $time_now . '\', INTERVAL '.$config->RAID_EGG_DURATION.' MINUTE) between date_start and date_end + OR DATE_ADD(\'' . $time_now . '\', INTERVAL '.$config->RAID_DURATION.' MINUTE) between date_start and date_end + ) + UNION + SELECT id, pokedex_id, pokemon_form_id + FROM pokemon + WHERE pokedex_id = :eggId + ORDER BY pokedex_id + ', ['raidLevel' => $raid_level, 'eggId' => $egg_id] + ); + // Add key for each raid level + $callbackData[0] = $action; + while ($pokemon = $rs->fetch()) { + $callbackData['p'] = $pokemon['id']; + $keys[] = button(get_local_pokemon_name($pokemon['pokedex_id'], $pokemon['pokemon_form_id']), $callbackData); + } - // Get the inline key array. - $keys = inline_key_array($keys, 1); + // Get the inline key array. + $keys = inline_key_array($keys, 1); - return $keys; + return $keys; } - -?> diff --git a/logic/raid_access_check.php b/logic/raid_access_check.php deleted file mode 100644 index 203a0dad..00000000 --- a/logic/raid_access_check.php +++ /dev/null @@ -1,53 +0,0 @@ -fetch(); - - // Check permissions - if ($rs->rowCount() == 0 or $update['callback_query']['from']['id'] != $raid['user_id']) { - // Check "-all" permission - debug_log('Checking permission:' . $permission . '-all'); - $permission = $permission . '-all'; - $raid_access = bot_access_check($update, $permission, $return_result); - } else { - // Check "-own" permission - debug_log('Checking permission:' . $permission . '-own'); - $permission_own = $permission . '-own'; - $permission_all = $permission . '-all'; - $raid_access = bot_access_check($update, $permission_own, true); - - // Check "-all" permission if we get "access denied" - // Maybe necessary if user has only "-all" configured, but not "-own" - if(!$raid_access) { - debug_log('Permission check for ' . $permission_own . ' failed! Maybe the access is just granted via ' . $permission . '-all ?'); - debug_log('Checking permission:' . $permission_all); - $raid_access = bot_access_check($update, $permission_all, $return_result); - } else { - $raid_access = bot_access_check($update, $permission_own, $return_result); - } - } - - // Return result - return $raid_access; -} - - -?> diff --git a/logic/raid_edit_gym_keys.php b/logic/raid_edit_gym_keys.php deleted file mode 100644 index 5868b04a..00000000 --- a/logic/raid_edit_gym_keys.php +++ /dev/null @@ -1,128 +0,0 @@ -RAID_CUSTOM_GYM_LETTERS) && $first_length == 1) { - // Explode special letters. - $special_keys = explode(',', $config->RAID_CUSTOM_GYM_LETTERS); - - foreach($special_keys as $id => $letter) - { - $letter = trim($letter); - debug_log($letter, 'Special gym letter:'); - // Fix chinese chars, prior: $length = strlen($letter); - $length = strlen(utf8_decode($letter)); - $not .= SP . "AND UPPER(LEFT(gym_name, " . $length . ")) != UPPER('" . $letter . "')" . SP; - } - } - $gymarea_query = ''; - if($gymarea_id != false) { - $json = json_decode(file_get_contents(CONFIG_PATH . '/geoconfig_gym_areas.json'),1); - $points = []; - foreach($json as $area) { - if($gymarea_id == $area['id']) { - foreach($area['path'] as $point) { - $points[] = $point[0].' '.$point[1]; - } - if($points[0] != $points[count($points)-1]) $points[] = $points[0]; - break; - } - } - $polygon_string = implode(',', $points); - $gymarea_query = "AND ST_CONTAINS(ST_GEOMFROMTEXT('POLYGON((".$polygon_string."))'), ST_GEOMFROMTEXT(CONCAT('POINT(',lat,' ',lon,')')))"; - } - // Show hidden gyms? - if($hidden == true) { - $show_gym = 0; - } else { - $show_gym = 1; - } - $query_collate = ""; - if($config->MYSQL_SORT_COLLATE != "") { - $query_collate = "COLLATE " . $config->MYSQL_SORT_COLLATE; - } - // Get gyms from database - $rs = my_query( - " - SELECT gyms.id, gyms.gym_name, gyms.ex_gym, - CASE WHEN SUM(raids.end_time > UTC_TIMESTAMP() - INTERVAL 10 MINUTE) THEN 1 ELSE 0 END AS active_raid - FROM gyms - LEFT JOIN raids - ON raids.gym_id = gyms.id - WHERE UPPER(LEFT(gym_name, $first_length)) = UPPER('{$first}') - $not - $gymarea_query - AND gyms.show_gym = {$show_gym} - GROUP BY gym_name, raids.gym_id, gyms.id, gyms.ex_gym - ORDER BY gym_name " . $query_collate . " - " - ); - - // Init empty keys array. - $keys = []; - - while ($gym = $rs->fetch()) { - // Add delete argument to keys - if ($delete == true) { - $arg = $gym['id'] . '-delete'; - } else { - $arg = $gym['id']; - } - - // List action to list only gyms with active raids, so always continue at the end - if ($action == 'list_raid') { - if ($gym['active_raid'] == 1) { - $keys[] = array( - 'text' => $gym['gym_name'], - 'callback_data' => '0:' . $action . ':' . $arg - ); - } - // Continue always in case of list action - continue; - } - - // Write to log. - // debug_log($gym); - - $active_raid = active_raid_duplication_check($gym['id']); - - // Show Ex-Gym-Marker? - if($config->RAID_CREATION_EX_GYM_MARKER && $gym['ex_gym'] == 1) { - $ex_raid_gym_marker = (strtolower($config->RAID_EX_GYM_MARKER) == 'icon') ? EMOJI_STAR : $config->RAID_EX_GYM_MARKER; - $gym_name = $ex_raid_gym_marker . SP . $gym['gym_name']; - } else { - $gym_name = $gym['gym_name']; - } - // Add warning emoji for active raid - if ($active_raid > 0) { - $gym_name = EMOJI_WARN . SP . $gym_name; - } - $keys[] = array( - 'text' => $gym_name, - 'callback_data' => $first . ':' . $action . ':' . $arg - ); - } - - // Get the inline key array. - $keys = inline_key_array($keys, 1); - - return $keys; - -} - -?> diff --git a/logic/raid_edit_gyms_first_letter_keys.php b/logic/raid_edit_gyms_first_letter_keys.php deleted file mode 100644 index c5c02206..00000000 --- a/logic/raid_edit_gyms_first_letter_keys.php +++ /dev/null @@ -1,179 +0,0 @@ -ENABLE_GYM_AREAS) { - $json = json_decode(file_get_contents(CONFIG_PATH . '/geoconfig_gym_areas.json'),1); - $points = []; - foreach($json as $area) { - $gymarea_id = ($gymarea_id !== false) ? $gymarea_id : $config->DEFAULT_GYM_AREA; - if($gymarea_id !== false && $gymarea_id == $area['id']) { - foreach($area['path'] as $point) { - $points[] = $point[0].' '.$point[1]; - } - $gymarea_name = $area['name']; - if($points[0] != $points[count($points)-1]) $points[] = $points[0]; - $skip_letter_keys = false; - } else { - $gymarea_keys[] = [ - 'text' => $area['name'], - 'callback_data' => $area['id'] . ':' . $gymarea_action . ':' . $action - ]; - } - } - $polygon_string = implode(',', $points); - $gymarea_query = "AND ST_CONTAINS(ST_GEOMFROMTEXT('POLYGON((".$polygon_string."))'), ST_GEOMFROMTEXT(CONCAT('POINT(',lat,' ',lon,')')))"; - } - // Init empty keys array. - $keys = []; - - if(!$skip_letter_keys or !$config->ENABLE_GYM_AREAS or $hidden) { - // Special/Custom gym letters? - if(!empty($config->RAID_CUSTOM_GYM_LETTERS)) { - // Explode special letters. - $special_keys = explode(',', $config->RAID_CUSTOM_GYM_LETTERS); - $select = 'SELECT CASE '; - foreach($special_keys as $letter) - { - $letter = trim($letter); - debug_log($letter, 'Special gym letter:'); - // Fix chinese chars, prior: $length = strlen($letter); - $length = strlen(utf8_decode($letter)); - $select .= SP . "WHEN UPPER(LEFT(gym_name, " . $length . ")) = '" . $letter . "' THEN UPPER(LEFT(gym_name, " . $length . "))" . SP; - } - $select .= 'ELSE UPPER(LEFT(gym_name, 1)) END AS first_letter'; - $group_order = 'GROUP BY 1 ORDER BY gym_name'; - }else { - $select = 'SELECT DISTINCT UPPER(SUBSTR(gym_name, 1, 1)) AS first_letter'; - $group_order = 'ORDER BY 1'; - } - // Show hidden gyms? - $show_gym = $hidden ? 0 : 1; - - if($action == 'list_by_gym') { - // Select only gyms with active raids - $query_condition = ' - LEFT JOIN raids - ON raids.gym_id = gyms.id - WHERE end_time > UTC_TIMESTAMP() - AND show_gym = ' . $show_gym; - }else { - $query_condition = 'WHERE show_gym = ' . $show_gym; - } - - $rs_count = my_query("SELECT COUNT(gym_name) as count FROM gyms {$query_condition} {$gymarea_query}"); - $gym_count = $rs_count->fetch(); - $rs = my_query( - " - {$select} - FROM gyms - {$query_condition} - {$gymarea_query} - {$group_order} - " - ); - // If found over 20 gyms, print letters - if($gym_count['count'] > 20) { - while ($gym = $rs->fetch()) { - // Add first letter to keys array - $keys[] = array( - 'text' => $gym['first_letter'], - 'callback_data' => $show_gym . ':' . $action . ':' . $gym['first_letter'] . (($gymarea_id !== 'n') ? ',' .$gymarea_id : '') - ); - } - - // Get the inline key array. - $keys = inline_key_array($keys, 4); - $letters = true; - }else { - // If less than 20 gyms was found, print gym names - if($action == 'list_by_gym') { - // Select only gyms with active raids - $query_condition = ' - WHERE end_time > UTC_TIMESTAMP() - AND show_gym = ' . $show_gym; - }else { - $query_condition = 'WHERE show_gym = ' . $show_gym; - } - $query_collate = ''; - if($config->MYSQL_SORT_COLLATE != "") { - $query_collate = "COLLATE " . $config->MYSQL_SORT_COLLATE; - } - $rs = my_query( - " - SELECT gyms.id, gyms.gym_name, gyms.ex_gym, - CASE WHEN SUM(raids.end_time > UTC_TIMESTAMP() - INTERVAL 10 MINUTE) THEN 1 ELSE 0 END AS active_raid - FROM gyms - LEFT JOIN raids - ON raids.gym_id = gyms.id - {$query_condition} - {$gymarea_query} - GROUP BY gym_name, raids.gym_id, gyms.id, gyms.ex_gym - ORDER BY gym_name " . $query_collate . " - " - ); - // Init empty keys array. - $keys = []; - - while ($gym = $rs->fetch()) { - if($gym['id'] != NULL) { - $active_raid = active_raid_duplication_check($gym['id']); - - // Show Ex-Gym-Marker? - if($config->RAID_CREATION_EX_GYM_MARKER && $gym['ex_gym'] == 1) { - $ex_raid_gym_marker = (strtolower($config->RAID_EX_GYM_MARKER) == 'icon') ? EMOJI_STAR : $config->RAID_EX_GYM_MARKER; - $gym_name = $ex_raid_gym_marker . SP . $gym['gym_name']; - } else { - $gym_name = $gym['gym_name']; - } - // Add warning emoji for active raid - if ($active_raid > 0) { - $gym_name = EMOJI_WARN . SP . $gym_name; - } - $keys[] = array( - 'text' => $gym_name, - 'callback_data' => 'gl' . $gymarea_id . ':' . $gym_name_action . ':' . $gym['id'] - ); - } - } - - // Get the inline key array. - $keys = inline_key_array($keys, 1); - } - } - - // Add back navigation key. - if($hidden == false) { - if($config->RAID_VIA_LOCATION_FUNCTION == 'remote') { - $query_remote = my_query('SELECT count(*) as count FROM raids LEFT JOIN gyms on raids.gym_id = gyms.id WHERE raids.end_time > (UTC_TIMESTAMP() - INTERVAL 10 MINUTE) AND temporary_gym = 1'); - if($query_remote->fetch()['count'] > 0) { - $keys[][] = array( - 'text' => getTranslation('remote_raids'), - 'callback_data' => '0:list_remote_gyms:0' - ); - } - } - $nav_keys = []; - if(!empty($gymarea_keys) && ($config->DEFAULT_GYM_AREA !== false || $gymarea_id === false)) $keys = array_merge($keys, inline_key_array($gymarea_keys, 2)); - - // Get the inline key array. - $keys[] = $nav_keys; - } - - return ['keys' => $keys, 'gymarea_name' => $gymarea_name, 'letters' => $letters]; -} - -?> diff --git a/logic/raid_edit_raidlevel_keys.php b/logic/raid_edit_raidlevel_keys.php index f06d0cab..3f783fcc 100644 --- a/logic/raid_edit_raidlevel_keys.php +++ b/logic/raid_edit_raidlevel_keys.php @@ -1,90 +1,84 @@ RAID_EGG_DURATION.' MINUTE) between date_start and date_end + OR DATE_ADD(UTC_TIMESTAMP(), INTERVAL '.$config->RAID_DURATION.' MINUTE) between date_start and date_end + ) + '.$query_event.' + GROUP BY raid_bosses.raid_level + ORDER BY (CASE WHEN raid_level = ' . RAID_ID_EX . ' THEN 0 ELSE 1 END) DESC, raid_level DESC + '; + // Get all raid levels from database + $rs_counts = my_query($query_counts); - if($event === false) { - // Set event ID to null if no event was selected - $event_id = 'N'; - $query_event = 'AND raid_bosses.raid_level != \'X\''; - }else { - $event_id = $event; - $query_event = ''; - } - $query_counts = ' - SELECT raid_level, COUNT(*) AS raid_level_count - FROM raid_bosses - WHERE ( - DATE_SUB(\'' . $time_now . '\', INTERVAL '.$config->RAID_EGG_DURATION.' MINUTE) between date_start and date_end - OR DATE_ADD(\'' . $time_now . '\', INTERVAL '.$config->RAID_DURATION.' MINUTE) between date_start and date_end - ) - '.$query_event.' - GROUP BY raid_bosses.raid_level - ORDER BY FIELD(raid_bosses.raid_level, \'6\', \'5\', \'4\', \'3\', \'2\', \'1\', \'X\') - '; - // Get all raid levels from database - $rs_counts = my_query($query_counts); - - // Init empty keys array. - $keys = []; - - // Add key for each raid level - while ($level = $rs_counts->fetch()) { - // Raid level and action - $raid_level = $level['raid_level']; + // Init empty keys array. + $keys = []; - // Add key for pokemon if we have just 1 pokemon for a level - if($level['raid_level_count'] == 1) { - $query_mon = my_query(' - SELECT pokemon.id, pokemon.pokedex_id, pokemon.pokemon_form_id - FROM raid_bosses - LEFT JOIN pokemon - ON pokemon.pokedex_id = raid_bosses.pokedex_id - AND pokemon.pokemon_form_id = raid_bosses.pokemon_form_id - WHERE ( - DATE_SUB(\'' . $time_now . '\', INTERVAL '.$config->RAID_EGG_DURATION.' MINUTE) between date_start and date_end - OR DATE_ADD(\'' . $time_now . '\', INTERVAL '.$config->RAID_DURATION.' MINUTE) between date_start and date_end - ) - AND raid_level = \''.$raid_level.'\' - '.$query_event.' - LIMIT 1 - '); - $pokemon = $query_mon->fetch(); - // Add key for pokemon - $keys[] = array( - 'text' => get_local_pokemon_name($pokemon['pokedex_id'], $pokemon['pokemon_form_id']), - 'callback_data' => $gym_id . ',' . $gym_first_letter . ':edit_starttime:' . $event_id . ',' . $raid_level . ',' . $pokemon['id'] - ); - } else { - // Add key for raid level - $keys[] = array( - 'text' => getTranslation($raid_level . 'stars'), - 'callback_data' => $gym_id . ',' . $gym_first_letter . ':edit_pokemon:' . $event_id . ',' . $raid_level - ); - } + // Add key for each raid level + $buttonData = $callbackData; + while ($level = $rs_counts->fetch()) { + if ($level['raid_level'] == 9 && $excludeElite) continue; + // Add key for pokemon if we have just 1 pokemon for a level + if($level['raid_level_count'] != 1) { + // Raid level and action + $buttonData[0] = 'edit_pokemon'; + $buttonData['rl'] = $level['raid_level']; + // Add key for raid level + $keys[] = button(getTranslation($level['raid_level'] . 'stars'), $buttonData); + continue; } - // Add key for raid event if user allowed to create event raids - if($admin_access[1] === true && $event === false) { - $keys[] = array( - 'text' => getTranslation('event'), - 'callback_data' => $gym_id . ',' . $gym_first_letter . ':edit_event:0' - ); - } - - // Get the inline key array. - $keys = inline_key_array($keys, 3); + $query_mon = my_query(' + SELECT pokemon.id, pokemon.pokedex_id, pokemon.pokemon_form_id + FROM raid_bosses + LEFT JOIN pokemon + ON pokemon.pokedex_id = raid_bosses.pokedex_id + AND pokemon.pokemon_form_id = raid_bosses.pokemon_form_id + WHERE ( + DATE_SUB(UTC_TIMESTAMP(), INTERVAL '.$config->RAID_EGG_DURATION.' MINUTE) between date_start and date_end + OR DATE_ADD(UTC_TIMESTAMP(), INTERVAL '.$config->RAID_DURATION.' MINUTE) between date_start and date_end + ) + AND raid_level = ? + '.$query_event.' + LIMIT 1 + ', [$level['raid_level']] + ); + $pokemon = $query_mon->fetch(); + $buttonData[0] = 'edit_starttime'; + $buttonData['rl'] = $level['raid_level']; + $buttonData['p'] = $pokemon['id']; + // Add key for pokemon + $keys[] = button(get_local_pokemon_name($pokemon['pokedex_id'], $pokemon['pokemon_form_id']), $buttonData); + unset($buttonData['p']); + } + // Add key for raid event if user allowed to create event raids + if(($admin_access[1] === true or $admin_access[0] === true) && $event === false) { + $eventData = $callbackData; + $eventData[0] = 'edit_event'; + $keys[] = button(getTranslation('event'), $eventData); + } - return $keys; -} + // Get the inline key array. + $keys = inline_key_array($keys, 3); -?> + return $keys; +} diff --git a/logic/raid_get_gyms_list_keys.php b/logic/raid_get_gyms_list_keys.php index 5d99b73e..111e7f12 100644 --- a/logic/raid_get_gyms_list_keys.php +++ b/logic/raid_get_gyms_list_keys.php @@ -6,54 +6,36 @@ */ function raid_get_gyms_list_keys($searchterm) { - // Init empty keys array. - $keys = []; - - // Make sure the search term is not empty - if(!empty($searchterm)) { - // Get gyms from database - $rs = my_query( - " - SELECT id, gym_name - FROM gyms - WHERE gym_name LIKE '$searchterm%' - AND show_gym LIKE 1 - OR gym_name LIKE '%$searchterm%' - AND show_gym LIKE 1 - ORDER BY - CASE - WHEN gym_name LIKE '$searchterm%' THEN 1 - WHEN gym_name LIKE '%$searchterm%' THEN 2 - ELSE 3 - END - LIMIT 15 - " - ); - - while ($gym = $rs->fetch()) { - $first = strtoupper(substr($gym['gym_name'], 0, 1)); - $keys[] = array( - 'text' => $gym['gym_name'], - 'callback_data' => $first . ':edit_raidlevel:' . $gym['id'] - ); - } - } - - // Add abort key. - if($keys) { - // Get the inline key array. - $keys = inline_key_array($keys, 1); - - // Add back navigation key. - $nav_keys = []; - $nav_keys[] = universal_inner_key($keys, '0', 'exit', '0', getTranslation('abort')); - - // Get the inline key array. - $keys[] = $nav_keys; - } - - return $keys; + // Get gyms from database + $rs = my_query(' + SELECT id, gym_name + FROM gyms + WHERE gym_name LIKE \'' . $searchterm . '%\' + AND show_gym LIKE 1 + OR gym_name LIKE \'% ' .$searchterm . '%\' + AND show_gym LIKE 1 + ORDER BY + CASE + WHEN gym_name LIKE \'' . $searchterm . '%\' THEN 1 + WHEN gym_name LIKE \'%' . $searchterm . '%\' THEN 2 + ELSE 3 + END + LIMIT 15 + ' + ); + // Init empty keys array. + $keys = []; + + while ($gym = $rs->fetch()) { + $first = strtoupper(substr($gym['gym_name'], 0, 1)); + $keys[] = button($gym['gym_name'], ['edit_raidlevel', 'g' => $gym['id'], 'fl' => $first]); + } + + // Add abort key. + if($keys) { + // Get the inline key array. + $keys = inline_key_array($keys, 1); + } + + return $keys; } - - -?> diff --git a/logic/raid_level.php b/logic/raid_level.php deleted file mode 100644 index a46d0f9a..00000000 --- a/logic/raid_level.php +++ /dev/null @@ -1,64 +0,0 @@ -fetch()) { - $raid_level = $level['raid_level']; - } - debug_log("Resolved level of {$pokedex_id}({$pokemon_form_id}) to {$raid_level}"); - } else { - info_log("Could not resolve level of {$pokedex_id}({$pokemon_form_id}), defaulting to 0!"); - $raid_level = '0'; - } - - return $raid_level; -} - -/** - * Get active raid bosses at a certain time. - * @param $time - string, datetime, local time - * @param $raid_level - ENUM('1', '2', '3', '4', '5', '6', 'X') - * @return string - */ -function get_raid_bosses($time, $raid_level) -{ - // Get raid level from database - $rs = my_query( - ' - SELECT pokedex_id, pokemon_form_id - FROM raid_bosses - WHERE \''.$time.'\' BETWEEN date_start AND date_end - AND raid_level = \''.$raid_level.'\' - '); - debug_log('Checking active raid bosses for raid level '.$raid_level.' at '.$time.':'); - $raid_bosses = []; - $egg_found = false; - while ($result = $rs->fetch()) { - $raid_bosses[] = $result; - if($result['pokedex_id'] == '999'.$raid_level) $egg_found = true; - debug_log('Pokedex id: '.$result['pokedex_id'].' | Form id: '.$result['pokemon_form_id']); - } - if(!$egg_found) $raid_bosses[] = ['pokedex_id' => '999'.$raid_level, 'pokemon_form_id' => 0]; // Add egg if it wasn't found from db - return $raid_bosses; -} - -?> diff --git a/logic/raid_list.php b/logic/raid_list.php index 904f6546..e9eab645 100644 --- a/logic/raid_list.php +++ b/logic/raid_list.php @@ -1,66 +1,58 @@ UTC_TIMESTAMP() - ORDER BY id DESC LIMIT 2 - " - ); - while ($answer_raids = $request->fetch()) { - $rows[] = get_raid($answer_raids['id']); - } + global $config; + // Init raid id. + $iqq = 0; + + // Botname:raid_id received? + if (substr_count($update['inline_query']['query'], ':') == 1) { + // Botname: received, is there a raid_id after : or not? + if(strlen(explode(':', $update['inline_query']['query'])[1]) != 0) { + // Raid ID. + $iqq = intval(explode(':', $update['inline_query']['query'])[1]); } - - - // Init array. - $contents = array(); - - // For each rows. - foreach ($rows as $key => $row) { - // Get raid poll. - $contents[$key]['text'] = show_raid_poll($row, true)['full']; - - // Set the title. - $contents[$key]['title'] = get_local_pokemon_name($row['pokemon'],$row['pokemon_form'], true) . ' ' . getPublicTranslation('from') . ' ' . dt2time($row['start_time']) . ' ' . getPublicTranslation('to') . ' ' . dt2time($row['end_time']); - - // Get inline keyboard. - $contents[$key]['keyboard'] = keys_vote($row); - - // Set the description. - $contents[$key]['desc'] = strval($row['gym_name']); - } - - debug_log($contents); - answerInlineQuery($update['inline_query']['id'], $contents); + } + + // Inline list polls. + $ids = [['id' => $iqq]]; + if ($iqq == 0) { + // If no id was given, search for two raids saved by the user + $request = my_query(' + SELECT id + FROM raids + WHERE user_id = ? + AND end_time>UTC_TIMESTAMP() + ORDER BY id DESC LIMIT 2 + ', [$update['inline_query']['from']['id']] + ); + $ids = $request->fetchAll(); + } + + $contents = []; + $i = 0; + foreach ($ids as $raid) { + $row = get_raid($raid['id']); + // Get raid poll. + $contents[$i]['text'] = show_raid_poll($row, true)['full']; + + // Set the title. + $contents[$i]['title'] = get_local_pokemon_name($row['pokemon'],$row['pokemon_form'], $config->LANGUAGE_PUBLIC) . ' ' . getPublicTranslation('from') . ' ' . dt2time($row['start_time']) . ' ' . getPublicTranslation('to') . ' ' . dt2time($row['end_time']); + + // Get inline keyboard. + $contents[$i]['keyboard'] = keys_vote($row); + + // Set the description. + $contents[$i]['desc'] = strval($row['gym_name']); + $i++; + } + + debug_log($contents); + answerInlineQuery($update['inline_query']['id'], $contents); } - -?> diff --git a/logic/raid_picture.php b/logic/raid_picture.php index bee70152..44218d9c 100644 --- a/logic/raid_picture.php +++ b/logic/raid_picture.php @@ -1,36 +1,678 @@ $raid['id'], + ':gym_id' => $raid['gym_id'], + ':pokedex_id' => $raid['pokemon'], + ':pokemon_form' => $raid['pokemon_form'], + ':standalone' => $standalone_photo, + ':ended' => $raid['raid_ended'], + ]; + $timeQuery = ''; + if($raid['raid_ended'] == 0) { + $timeQuery = ' + AND start_time = :start_time + AND end_time = :end_time'; + $binds['start_time'] = $raid['start_time']; + $binds['end_time'] = $raid['end_time']; + } + $query_cache = my_query(' + SELECT id, unique_id + FROM photo_cache + WHERE raid_id = :raid_id + AND gym_id = :gym_id + AND pokedex_id = :pokedex_id + AND form_id = :pokemon_form + ' . $timeQuery . ' + AND ended = :ended + AND standalone = :standalone + LIMIT 1', $binds + ); + + if($query_cache->rowCount() > 0) { + $result = $query_cache->fetch(); + return [false, $result['id'], $result['unique_id']]; + } + return [true, create_raid_picture($raid, $standalone_photo)]; +} /** - * Get full raidpicture.php URL + * Create a raid picture and return it as a string * @param array $raid Raid array from get_raid() - * @param bool $standalone Clear the bottom right corner of the photo from text + * @param bool $standalone_photo Clear the bottom right corner of the photo from text + * @param bool $debug Add debug features to the photo * @return string */ -function raid_picture_url($raid, $standalone = false) -{ +function create_raid_picture($raid, $standalone_photo = false, $debug = false) { global $config; + if ($GLOBALS['metrics']){ + $GLOBALS['requests_total']->inc(['raidpicture']); + } - // If any params go missing from the url the image generated will likely be served from cache - // So let's warn people if were generating bogus URLs - foreach (array('pokemon', 'pokemon_form', 'start_time', 'end_time', 'gym_id') as $key) { - if (!array_key_exists($key, $raid) || $raid[$key] == '' || $raid[$key] == '-') { - error_log("raid_picture; Insufficient parameters for raidpicture: '{$key}:{$raid[$key]}'"); - } + // Query missing raid info + $q_pokemon_info = my_query(' + SELECT + pokemon_name, pokemon_form_name, min_cp, max_cp, min_weather_cp, max_weather_cp, weather, shiny, type, type2, + (SELECT img_url FROM gyms WHERE id=:gymId LIMIT 1) as img_url + FROM pokemon + WHERE pokedex_id = :pokemonId + AND pokemon_form_id = :pokemonForm LIMIT 1 + ',[ + 'gymId' => $raid['gym_id'], + 'pokemonId' => $raid['pokemon'], + 'pokemonForm' => $raid['pokemon_form'], + ]); + if($q_pokemon_info->rowCount() == 0) { + info_log("Something wrong with the raid data provided!"); + info_log(print_r($raid,true)); + exit(); } + $raid = array_merge($raid, $q_pokemon_info->fetch()); - if(utcnow() > $raid['end_time']) { - // We'll set raid start and end times to 0 to get a new image for when the raid has ended. - // Setting thetimes to 0 also makes it so TG can cache the raid ended -images and reuse them - $start_time = $end_time = 0; + // Fonts + $font_gym = FONTS_PATH . '/' . $config->RAID_PICTURE_FONT_GYM; + $font_text = FONTS_PATH . '/' . $config->RAID_PICTURE_FONT_TEXT; + $font_ex_gym = FONTS_PATH . '/' . $config->RAID_PICTURE_FONT_EX_GYM; + + // Canvas size + $canvas_width = 700; + $canvas_height = 356; + + // Creating an empty canvas + $canvas = imagecreatetruecolor($canvas_width,$canvas_height); + imagesavealpha($canvas,true); + + // Background color + // Default: White + $bg_rgb = [255,255,255]; + $config_bg_color = explode(',',$config->RAID_PICTURE_BG_COLOR); + if(count($config_bg_color) == 3) { + $bg_rgb = $config_bg_color; + } else { + info_log($config->RAID_PICTURE_BG_COLOR, 'Invalid value RAID_PICTURE_BG_COLOR:'); + } + $bg_color = imagecolorallocate($canvas,$bg_rgb[0],$bg_rgb[1], $bg_rgb[2]); + imagefill($canvas, 0, 0, $bg_color); + + // Text / Font color + // Default: Black + $font_rgb = [0,0,0]; + $config_font_color = explode(',',$config->RAID_PICTURE_TEXT_COLOR); + if(count($config_font_color) == 3) { + $font_rgb = $config_font_color; + } else { + info_log($config->RAID_PICTURE_TEXT_COLOR, 'Invalid value RAID_PICTURE_TEXT_COLOR:'); + } + $font_color = imagecolorallocate($canvas,$font_rgb[0],$font_rgb[1],$font_rgb[2]); + + // Defining RBG values that are used to create transparent color + // Should be different from RAID_PICTURE_BG_COLOR and RAID_PICTURE_TEXT_COLOR + $transparent_rgb = [0,255,0]; + + // Gym image + $gym_url = $raid['img_url']; + $gym_image_path = ''; + if($config->RAID_PICTURE_STORE_GYM_IMAGES_LOCALLY && !empty($gym_url)) { + if(substr($gym_url, 0, 7) == 'file://') { + $gym_image_path = $gym_url; + debug_log($gym_image_path, 'Found an image imported via a portal bot: '); + }else { + $file_name = explode('/', $gym_url)[3]; + $gym_image_path = PORTAL_IMAGES_PATH .'/'. $file_name.'.png'; + debug_log($gym_image_path, 'Attempting to use locally stored gym image'); + if(!file_exists($gym_image_path)) { + debug_log($gym_url, 'Gym image not found, attempting to downloading it from: '); + if(is_writable(PORTAL_IMAGES_PATH)) { + download_Portal_Image($gym_url, PORTAL_IMAGES_PATH, $file_name . '.png'); + }else { + $gym_image_path = $gym_url; + info_log(PORTAL_IMAGES_PATH, 'Failed to write new gym image, incorrect permissions in directory '); + } + } + } }else { - $start_time = strtotime($raid['start_time']); - $end_time = strtotime($raid['end_time']); - } - if($raid['event'] == EVENT_ID_EX) $ex_raid = '1'; else $ex_raid = '0'; - $picture_url = "{$config->RAID_PICTURE_URL}?pokemon={$raid['pokemon']}&pokemon_form={$raid['pokemon_form']}&gym_id={$raid['gym_id']}&start_time={$start_time}&end_time={$end_time}&ex_raid={$ex_raid}"; - if($standalone) $picture_url .= '&sa=1'; - if($raid['costume'] != 0) $picture_url .= '&costume='.$raid['costume']; - debug_log('raid_picture_url: ' . $picture_url); - return $picture_url; + $img_gym = false; + if (!empty($gym_url)) { + $gym_image_path = $gym_url; + } + } + $img_gym = $gym_image_path != '' ? grab_img($gym_image_path) : false; + if($img_gym == false) { + info_log($gym_image_path, 'Loading the gym image failed, using default gym image'); + if(is_file($config->RAID_DEFAULT_PICTURE)) { + $img_gym = grab_img($config->RAID_DEFAULT_PICTURE); + } else { + info_log($config->RAID_DEFAULT_PICTURE, 'Cannot read default gym image:'); + $img_gym = grab_img(IMAGES_PATH . "/gym_default.png"); + } + } + + // Get the width and height of the gym picture + $gym_w = imagesx($img_gym); + $gym_h = imagesy($img_gym); + + // Crop gym image + if($gym_w > $gym_h) { + $size = $gym_h; + $crop_x = floor((($gym_w/2)-($gym_h/2))); + $crop_y = 0; + } else { + $size = $gym_w; + $crop_x = 0; + $crop_y = floor((($gym_h/2)-($gym_w/2))); + } + + // Create mask + $new_w = 300; + $new_h = 300; + $mask = imagecreatetruecolor($new_w,$new_h); + + // Fill the mask with background color + $bg = imagecolorallocate($mask,$bg_rgb[0],$bg_rgb[1], $bg_rgb[1]); + imagefill($mask,0,0,$bg); + + // Define transparent color for the mask + $transparent = imagecolorallocate($mask,$transparent_rgb[0],$transparent_rgb[1],$transparent_rgb[2]); + imagecolortransparent($mask,$transparent); + + // Creating the orange circle around the gym photo + $color_ellipse = imagecolorallocate($mask,254,193,161); + imagefilledellipse($mask,$new_w/2,$new_h/2,$new_w-9,$new_h-9,$color_ellipse); + imagefilledellipse($mask,$new_w/2,$new_h/2,$new_w-16,$new_h-16,$bg); + + // Creating a circle that is filled with transparent color + imagefilledellipse($mask,$new_w/2,$new_h/2,$new_w-30,$new_h-30,$transparent); + + // Merging the desired part of the gym picture with canvas + imagecopyresampled($canvas,$img_gym,0,0,$crop_x,$crop_y,$new_w,$new_h, $size,$size); + + // Merging the mask with a circular cutout to the canvas + imagecopymerge($canvas, $mask, 0, 0, 0, 0, $new_w, $new_h, 100); + + + + // Is ex gym? + if($raid['ex_gym'] == 1) { + $ex_text_size = 20; + $ex_text_angle = 0; + $corner = 16; // Roundness of the corners + $extra = $ex_text_size/5+1; // Some extra height + + $ex_mark_bg_color = [94,169,190]; + $ex_mark_text_color = [255,255,255]; + + // Get the text with local translation for EX Raid gym + $ex_raid_gym_text = strtoupper(getPublicTranslation('ex_gym')); + // Finding out the size of text + $ex_text_box = imagettfbbox($ex_text_size,$ex_text_angle,$font_ex_gym,$ex_raid_gym_text); + + $ex_logo_width = $ex_text_box[2]+($corner); + $ex_logo_height = $ex_text_size+$extra; + + // Create the canvas for EX RAID indicator + $ex_logo = imagecreatetruecolor($ex_logo_width,$ex_logo_height); + // Defining the transparent color + $ex_transparent = imagecolorallocate($ex_logo,$transparent_rgb[0],$transparent_rgb[1],$transparent_rgb[2]); + imagecolortransparent($ex_logo,$ex_transparent); + // Defining background color + $ex_logo_bg = imagecolorallocate($mask,$ex_mark_bg_color[0],$ex_mark_bg_color[1], $ex_mark_bg_color[2]); + $ex_text_color = imagecolorallocate($ex_logo,$ex_mark_text_color[0],$ex_mark_text_color[1],$ex_mark_text_color[2]); + + //Filling the canvas with transparent color + imagefill($ex_logo,0,0,$ex_transparent); + + // Creating 4 balls, one in each corner + imagefilledellipse($ex_logo,$corner/2,$corner/2,$corner,$corner,$ex_logo_bg); + imagefilledellipse($ex_logo,$corner/2,$ex_logo_height-$corner/2,$corner,$corner,$ex_logo_bg); + imagefilledellipse($ex_logo,$ex_logo_width-$corner/2,$corner/2,$corner,$corner,$ex_logo_bg); + imagefilledellipse($ex_logo,$ex_logo_width-$corner/2,$ex_logo_height-$corner/2,$corner,$corner,$ex_logo_bg); + // And two rectangles to fill the rest + imagefilledrectangle($ex_logo,$corner/2,0,$ex_logo_width-($corner/2),$ex_logo_height,$ex_logo_bg); + imagefilledrectangle($ex_logo,0,$corner/2,$ex_logo_width,$ex_logo_height-($corner/2),$ex_logo_bg); + + // Draw the text + imagettftext($ex_logo,$ex_text_size,$ex_text_angle,$corner/2,$ex_text_size+1,$ex_text_color,$font_ex_gym,$ex_raid_gym_text); + + // Copy icon into canvas + imagecopy($canvas,$ex_logo,20,20,0,0,$ex_logo_width,$ex_logo_height); + } + + + $show_boss_pokemon_types = false; + // Raid running + if(!$raid['raid_ended']) { + // Raid Egg + if($raid['pokemon'] > 9990) { + // Getting the actual icon + $img_pokemon = grab_img(IMAGES_PATH . "/raid_eggs/pokemon_icon_" . $raid['pokemon'] . "_00.png"); + + // Position and size of the picture + $dst_x = $dst_y = 150; + $dst_w = $dst_h = 200; + if(in_array($raid['level'], RAID_LEVEL_SHADOW)) + $src_w = $src_h = 150; + else + $src_w = $src_h = 128; + + //Pokemon + } else { + // Check pokemon icon source and create image + $img_file = null; + $addressableFilename = $uiconsFilename = ['','','','','','']; + $p_sources = explode(',', $config->RAID_PICTURE_POKEMON_ICONS); + + $addressableFilename[0] = 'pm'.$raid['pokemon']; + $uiconsFilename[0] = $raid['pokemon']; + if($raid['pokemon_form_name'] != 'normal') { + $addressableFilename[1] = '.f' . strtoupper($raid['pokemon_form_name']); + $addressableFilename[5] = '.f' . str_replace(strtoupper($raid['pokemon_name']).'_', '', strtoupper($raid['pokemon_form_name'])); + $uiconsFilename[1] = '_f' . $raid['pokemon_form']; + } + + // Add costume info for every mon except megas + if($raid['costume'] != 0 && $raid['pokemon_form'] >= 0) { + $costume = json_decode(file_get_contents(ROOT_PATH . '/protos/costume.json'), true); + $costumeName = array_search($raid['costume'],$costume); + if(!empty($costumeName)) { + $addressableFilename[2] = '.c' . $costumeName; + $uiconsFilename[2] = '_c' . $raid['costume']; + } + } + if($raid['shiny'] == 1 && $config->RAID_PICTURE_SHOW_SHINY) { + $addressableFilename[3] = '.s'; + $uiconsFilename[3] = '_s'; + $shiny_icon = grab_img(IMAGES_PATH . "/shinystars.png"); + } + $addressableFilename[4] = '.icon.png'; + $uiconsFilename[4] = '.png'; + $imageFilenames = createFilenameList($addressableFilename, $uiconsFilename); + foreach($p_sources as $p_dir) { + // Icon dir named 'pokemon'? Then change path to not add '_repo-owner' to icon folder name + if($p_dir == 'pokemon') $asset_dir = 'pokemon'; else $asset_dir = 'pokemon_' . $p_dir; + // Set pokemon icon dir + $p_img_base_path = IMAGES_PATH . "/" . $asset_dir; + + // Check if file exists in this collection + foreach($imageFilenames as $filename) { + if(file_exists($p_img_base_path . "/" . $filename) && filesize($p_img_base_path . "/" . $filename) > 0) { + $img_file = $p_img_base_path . "/" . $filename; + break 2; + } + } + } + + // If no image was found, substitute with a fallback + if($img_file === null) { + info_log(join($addressableFilename) . ' ' . join($uiconsFilename), 'Failed to find an image in any pokemon image collection for:'); + $img_fallback_file = null; + // If we know the raid level, fallback to egg image + if(array_key_exists('level', $raid) && $raid['level'] !== null && $raid['level'] != 0) { + $img_fallback_file = IMAGES_PATH . "/raid_eggs/pokemon_icon_999" . $raid['level'] . "_00.png"; + } else { + info_log('Unknown raid level, using fallback icon.'); + $img_fallback_file = $config->RAID_PICTURE_POKEMON_FALLBACK; + } + $img_file = $img_fallback_file; + } + + $img_pokemon = grab_img($img_file); + + // Position and size of the picture + $dst_x = $dst_y = 100; + $dst_w = $dst_h = 256; + [$src_w, $src_h] = getimagesize($img_file); + + if($raid['type'] != '') $show_boss_pokemon_types = true; + } + + // Raid ended + } else { + // Raid won image + $img_pokemon = grab_img(IMAGES_PATH . "/raidwon.png"); + + // Position and size of the picture + $dst_x = $dst_y = 172; + $src_w = 444; + $src_h = 512; + $dst_w = 160; + $dst_h = floor($dst_w/$src_w*$src_h); + } + + // Create pokemon image. + imagesavealpha($img_pokemon,true); + + // Debug - Add border around pokemon image + if($debug) { + $im = imagecreate($src_w,$src_h); + $black = imagecolorallocate($im,0,0,0); + imagerectangle($img_pokemon,0,0,$src_w-1,$src_h-1,$black); + } + + // Add pokemon to image + imagecopyresampled($canvas,$img_pokemon,$dst_x,$dst_y,0,0,$dst_w,$dst_h,$src_w,$src_h); + if(isset($raid['shadow']) && $raid['shadow'] && !in_array($raid['pokemon'], EGGS)) { + $img_shadow = grab_img(IMAGES_PATH . '/shadow.png'); + $icon_x = 275; + imagecopyresampled($canvas,$img_shadow,$icon_x,275,0,0,75,75,55,62); + $icon_x -= 45; + } + + // Add pokemon types + if($config->RAID_PICTURE_POKEMON_TYPES && $show_boss_pokemon_types) { + $img_type = grab_img(IMAGES_PATH . "/types/".$raid['type'].".png"); + $icon_x = $icon_x ?? 300; + imagesavealpha($img_type, true); + if($raid['type2'] != '') { + $img_type2 = grab_img(IMAGES_PATH . "/types/".$raid['type2'].".png"); + imagesavealpha($img_type2, true); + imagecopyresampled($canvas,$img_type2,$icon_x,300,0,0,40,40,64,64); + $icon_x -= 50; + } + imagecopyresampled($canvas,$img_type,$icon_x,300,0,0,40,40,64,64); + } + if(isset($shiny_icon)) { + imagesavealpha($shiny_icon,true); + $light_white = imagecolorallocatealpha($canvas, 255,255,255,50); + imagefilledellipse($canvas, $icon_x-35 ,320,40,40,$light_white); + imagecopyresampled($canvas,$shiny_icon,$icon_x-52,301,0,0,35,35,100,100); + } + + // Ex-Raid? + if($raid['event'] == EVENT_ID_EX) { + $img_expass = grab_img(IMAGES_PATH . "/expass.png"); + imagesavealpha($img_expass,true); + + // Debug - Add border around expass image + if($debug) { + $im = imagecreate(256,256); + $black = imagecolorallocate($im,0,0,0); + imagerectangle($img_expass,0,0,255,255,$black); + } + imagecopyresampled($canvas,$img_expass,0,225,0,0,100,100,256,256); + } + + + + // Adding the gym name to the image + $text_size = 23; // Font size of additional text + $text_size_cp_weather = 20;// Font size of weather cp text + $left_after_poke = 356; // First left position behind the pokemon icon. + $angle = 0; // Angle of the text + $spacing = 10; // Spacing between lines + $spacing_right = 10; // Empty space on the right for weather icons and CP text + + + + // Gym name + // Largest gym name we found so far for testing: + //$gym_name = 'Zentrum für Junge Erwachsene der Kirche Jesu Christi der Heiligen der Letzten Tage Pfahl Düsseldorf'; + $gym_name = mb_convert_encoding($raid['gym_name'], 'ISO-8859-1'); + + // Get length, the shortest and largest word of the gym name + $gym_name_words = explode(SP, $gym_name); + $gym_name_word_lengths = array_map('strlen', $gym_name_words); + $gym_name_word_largest = max($gym_name_word_lengths); + $gym_name_total_chars = strlen(mb_convert_encoding($gym_name, 'ISO-8859-1', 'UTF-8')); + + // Number of rows based on number of words or total chars + $gym_name_rows = 1; + if(count($gym_name_words) > 1 && $gym_name_total_chars >= 18 && $gym_name_total_chars <= 50) { + $gym_name_rows = 2; + } else if($gym_name_total_chars > 50) { + $gym_name_rows = 3; + } + + // Wrap gym name to multiple lines if too long + $gym_name_lines = explode(PHP_EOL,wordwrap(trim($gym_name),floor(($gym_name_total_chars+$gym_name_word_largest)/$gym_name_rows),PHP_EOL)); + + debug_log($gym_name_total_chars, 'Gym name length:'); + debug_log($gym_name_lines, 'Gym name lines:'); + + // Target width and height + $targetWidth = imagesx($canvas) - imagesx($mask) - $spacing_right; + $targetHeight = 95; + $rowHeight = $targetHeight/$gym_name_rows; + + // Get largest possible fontsize for each gym name line + $fontsize_gym = 0; + for($l=0; $l= $targetWidth || $height >= $rowHeight){ + break; + } + } + + // Gym name font size and spacing + if($l == 0 || $targetsize < $fontsize_gym) { + $fontsize_gym = $targetsize; + $spacing_gym = $height * 0.30; + } + } + + // Add gym name to image + for($y=0;$yLANGUAGE_PUBLIC, true); + } else { + $time_text = getPublicTranslation('raid_done'); + } + + // Adjust margins, font size and raid time text itself + $time_text_lines = array(); + if(strpos($time_text, ',') !== false) { + $time_text_lines[] = explode(',', $time_text)[0] . ','; + // Thursday, 18:00 - 18:45 + if(count(explode(SP, explode(',', $time_text, 2)[1])) == 4) { + $time_top = 150; + $time_text_size = 35; + $tmp_time_text_line = explode(SP, explode(',', $time_text)[1]); + $time_text_lines[] = $tmp_time_text_line[0] . SP . $tmp_time_text_line[1] . SP . $tmp_time_text_line[2] . SP . $tmp_time_text_line[3]; + + // Thursday, 12. December 18:00 - 18:45 + } else { + $time_top = 140; + $time_text_size = 30; + $tmp_time_text_line = explode(SP, explode(',', $time_text)[1]); + $time_text_lines[] = $tmp_time_text_line[0] . SP . $tmp_time_text_line[1] . SP . $tmp_time_text_line[2]; + $time_text_lines[] = $tmp_time_text_line[3] . SP . $tmp_time_text_line[4] . SP . $tmp_time_text_line[5]; + } + } else { + // 18:00 - 18:45 or raid ended text. + $time_text_size = 40; + $time_top = 175; + $time_text_lines[] = $time_text; + } + $num_text_lines = count($time_text_lines); + // If the photo is sent without caption, we want to keep the bottom right corcer clear of text because Telegram covers it with a timestamp + if($standalone_photo) $time_top -= 10; + + // Go through every line... + for($ya=0;$ya<$num_text_lines;$ya++){ + // ...and draw them to image + $time_text_top = ($time_top+($ya*($time_text_size+$spacing))); + + // Align text to center between pokemon icon and right edge + $box = imagettfbbox($time_text_size, $angle, $font_text, $time_text_lines[$ya]); + $min_x = min(array($box[0], $box[2], $box[4], $box[6])); + $max_x = max(array($box[0], $box[2], $box[4], $box[6])); + $textwidth = ($max_x - $min_x); + $time_left = $left_after_poke + floor((((imagesx($canvas) - $left_after_poke - $spacing_right) - $textwidth)/2)); + imagettftext($canvas,$time_text_size,$angle,$time_left,$time_text_top,$font_color,$font_text,$time_text_lines[$ya]); + } + + + + // Pokemon raid boss + $pokemon_name = get_local_pokemon_name($raid['pokemon'], $raid['pokemon_form'], $config->LANGUAGE_PUBLIC); + if(!in_array($raid['pokemon'], EGGS) && isset($raid['shadow']) && $raid['shadow']) $pokemon_name .= ' ' . getPublicTranslation('pokemon_form_shadow'); + + // Pokemon name and form? + $pokemon_text_lines = array($pokemon_name); + if(strlen($pokemon_name) > 20) { + $pokemon_text_lines = explode(SP,$pokemon_name, 2); + if(count($pokemon_text_lines) == 1) { + // Wrapping the time text if too long (to 20 letters) + $pokemon_text_lines = explode(PHP_EOL,wordwrap(trim($pokemon_name),20,PHP_EOL)); + } + } + $num_pokemon_lines = count($pokemon_text_lines); + + // Target width and height + $targetWidth = imagesx($canvas) - $left_after_poke - (strlen($raid['weather']) * 42) - $spacing_right; + $targetHeight = 80; + + // Get largest possible fontsize for each pokemon name and form line + $fontsize_poke = 0; + for($p=0; $p<($num_pokemon_lines); $p++) { + for($s=1; $s<40; $s=$s+0.5){ + $box = imagettfbbox($s, 0, $font_text, $pokemon_text_lines[$p]); + $min_x = min(array($box[0], $box[2], $box[4], $box[6])); + $max_x = max(array($box[0], $box[2], $box[4], $box[6])); + $min_y = min(array($box[1], $box[3], $box[5], $box[7])); + $max_y = max(array($box[1], $box[3], $box[5], $box[7])); + $width = ($max_x - $min_x); + $height = ($max_y - $min_y); + $targetsize = $s; + // Exit once we exceed width or height + if($width >= $targetWidth || $height >= $targetHeight){ + break; + } + } + + // Gym name font size and spacing + if($p == 0 || $targetsize < $fontsize_poke) { + $fontsize_poke = $targetsize; + } + } + + // Pokemon name (and form) in 1 row + $poke_text_top = 310; + + // Pokemon name and form in one or two lines? + if($num_pokemon_lines > 1) { + $poke_text_top = 272; + } + + // If the photo is sent without caption, we want to keep the bottom right corcer clear of text because Telegram covers it with a timestamp + if($standalone_photo) $poke_text_top -= 50; + + // Add pokemon name to image + for($pa=0;$pa<$num_pokemon_lines;$pa++){ + // Get text width and height + $textwidth = ($max_x - $min_x); + $textheight = ($max_y - $min_y); + // Position from top + $poke_text_top = floor($poke_text_top+($pa*($fontsize_poke+$spacing))); + imagettftext($canvas,$fontsize_poke,$angle,$left_after_poke,$poke_text_top,$font_color,$font_text,$pokemon_text_lines[$pa]); + } + + // Pokemon CP + if($raid['pokemon'] < 9990) { + $cp_text_top = $poke_text_top+$text_size+$spacing; + $cp_text = $raid['min_cp']." - ".$raid['max_cp']; + $cp_text2 = "(".$raid['min_weather_cp']."-".$raid['max_weather_cp'].")"; + + imagettftext($canvas,$text_size,$angle,$left_after_poke,$cp_text_top,$font_color,$font_text,$cp_text); + $cp_weather_text_box = imagettfbbox($text_size_cp_weather,$angle,$font_text,$cp_text2); + imagettftext($canvas,$text_size_cp_weather,$angle,($canvas_width-$cp_weather_text_box[2]-$spacing_right),$cp_text_top,$font_color,$font_text,$cp_text2); + + $count_weather = strlen($raid['weather']); + for($i=0;$i<$count_weather;$i++) { + $we = substr($raid['weather'],$i,1); + $weather_icon_path = IMAGES_PATH . "/weather/"; + // Use white icons? + if($config->RAID_PICTURE_ICONS_WHITE) { + $weather_icon_path = IMAGES_PATH . "/weather_white/"; + } + $weather_icon = grab_img($weather_icon_path . $we . ".png"); // 64x64 + imagecopyresampled($canvas,$weather_icon,$canvas_width-$spacing_right-($count_weather-$i)*40,$poke_text_top-30,0,0,38,38,64,64); + } + } + + ob_start(); + // Define and print picture + // PNG + if($config->RAID_PICTURE_FILE_FORMAT == 'png') { + imagepng($canvas); + + // JPEG + } else if($config->RAID_PICTURE_FILE_FORMAT == 'jpeg' || $config->RAID_PICTURE_FILE_FORMAT == 'jpg') { + imagejpeg($canvas, NULL, 90); + + // Use GIF as default - smallest file size without compression + } else { + imagegif($canvas); + } + return ob_get_clean(); + +} + +/** + * Create GD image object from given URI regardless of file type + * @param string $uri Image uri + * @return bool|object + */ +function grab_img($uri) { + try { + $img = imagecreatefromstring(file_get_contents($uri)); + }catch(Exception $e) { + info_log($uri, 'Failed to get image:'); + return false; + } + return $img; +} + +function createFilenameList($a, $u) { + // Full filename of addressable icon + $filenames[] = join([$a[0], $a[1], $a[2], $a[3], $a[5]]); + // Full filename of uicons icon + $filenames[] = join($u); + // List of fallback icons for addressable assets + $filenames[] = join([$a[0], $a[5], $a[2], $a[3], $a[4]]); + $filenames[] = join([$a[0], '.fNORMAL', $a[2], $a[3], $a[4]]); + $filenames[] = join([$a[0], $a[1], $a[2], $a[4]]); + $filenames[] = join([$a[0], '.fNORMAL', $a[2], $a[4]]); + $filenames[] = join([$a[0], $a[1], $a[4]]); + $filenames[] = join([$a[0], '.fNORMAL', $a[4]]); + $filenames[] = join([$a[0], $a[4]]); + $filenames[] = join([$a[0], $a[4]]); + + return $filenames; } -?> diff --git a/logic/raid_poll_message.php b/logic/raid_poll_message.php index 26156c00..6621cece 100644 --- a/logic/raid_poll_message.php +++ b/logic/raid_poll_message.php @@ -8,24 +8,22 @@ */ function raid_poll_message($msg_array, $append, $skip = false) { - global $config; - // Array key full already created? - if(!(array_key_exists('full', $msg_array))) { - $msg_array['full'] = ''; - } - - //Raid picture? - $msg_array['full'] .= $append; - if($config->RAID_PICTURE && $skip == false) { - // Array key short already created? - if(!(array_key_exists('short', $msg_array))) { - $msg_array['short'] = ''; - } + global $config; + // Array key full already created? + if(!(array_key_exists('full', $msg_array))) { + $msg_array['full'] = ''; + } - $msg_array['short'] .= $append; + //Raid picture? + $msg_array['full'] .= $append; + if($config->RAID_PICTURE && $skip == false) { + // Array key short already created? + if(!(array_key_exists('short', $msg_array))) { + $msg_array['short'] = ''; } - return $msg_array; -} + $msg_array['short'] .= $append; + } -?> + return $msg_array; +} diff --git a/logic/read_upcoming_bosses.php b/logic/read_upcoming_bosses.php index 09c0670c..7b5ff297 100644 --- a/logic/read_upcoming_bosses.php +++ b/logic/read_upcoming_bosses.php @@ -1,64 +1,89 @@ TIMEZONE); - $transitions = ( $now->getTransitions()[0]['isdst'] ? 0 : 1 ); - $tz_offset = (-7 - $transitions)*60*60; - $count = 0; - $sql = $list = $prev_start = $prev_rl = ''; - foreach($pb['breakingNews'] as $news) { - if($news['type'] == 'RAID_TYPE_RAID') { - $rl = str_replace('RAID_LEVEL_','', $news['tier']); - if($rl == "MEGA") $raid_level_id = 6; else $raid_level_id = $rl; - if($raid_level_id != '5' and $raid_level_id != '6') break; // Limit scheduling to tier 5 and mega only - $starttime = new DateTime("@".(substr($news['startDate'],0,10) + $tz_offset), new dateTimeZone('UTC')); - $endtime = new DateTime("@".(substr($news['endDate'],0,10) + $tz_offset), new dateTimeZone('UTC')); + $pb_timezone = new dateTimeZone('America/Los_Angeles'); + $standardTimezone = new dateTimeZone('UTC'); + $count = 0; + $sql = $list = $prev_start = $prev_end = $prev_rl = ''; + $returnArr = []; + foreach($pb['breakingNews'] as $news) { + if($news['type'] != 'RAID_TYPE_RAID') continue; - // If the boss only appears for an hour, the eggs most likely start to spawn 20 minutes prior to the time. - $diff = $starttime->diff($endtime); - if($diff->format('%h') == 1) { - $starttime->sub(new DateInterval('PT20M')); - } - $date_start = $starttime->format('Y-m-d H:i:s'); + $rl = str_replace('RAID_LEVEL_','', $news['tier']); + $raid_level_id = array_search($rl, $pokebattler_level_map); - if($endtime->format('H') == '11') { - // Usually the switch happens at 10. Pokebattler sets the end time to 11, so we must manually set it to 10 - $date_end = $endtime->format('Y-m-d').' 10:00:00'; - }else { - $date_end = $endtime->format('Y-m-d H:i:s'); - } + $levelLimiter = !$levelsToRead ? $pokebattler_import_future_tiers : $levelsToRead; + if(!in_array($raid_level_id, $levelLimiter)) continue; // Limit scheduling to tier 5 and higher only - $dex_id_form = explode('-',resolve_boss_name_to_ids($news['pokemon']),2); - if($prev_start != $date_start) { - $list.= CR . '' . $date_start . ' - ' . $date_end . ':' . CR; - } - if($prev_rl != $raid_level_id) { - $list.= '' . getTranslation($raid_level_id . 'stars') .':' . CR; - } - $list.= get_local_pokemon_name($dex_id_form[0], $dex_id_form[1]) . CR; - $prev_start = $date_start; - $prev_rl = $raid_level_id; + $starttime = new DateTime("@".(substr($news['startDate'],0,10)), $standardTimezone); + $endtime = new DateTime("@".(substr($news['endDate'],0,10)), $standardTimezone); - if($count == 0) { - $count++; - $sql .= 'INSERT INTO raid_bosses (pokedex_id, pokemon_form_id, date_start, date_end, raid_level, scheduled) VALUES '; - $sql .= '("'.$dex_id_form[0].'","'.$dex_id_form[1].'","'.$date_start.'","'.$date_end.'","'.$raid_level_id.'", 1)'; - }else { - $sql .= ',("'.$dex_id_form[0].'","'.$dex_id_form[1].'","'.$date_start.'","'.$date_end.'","'.$raid_level_id.'", 1)'; - } - } + $starttime->setTimezone($pb_timezone); + $endtime->setTimezone($pb_timezone); + + // If the boss only appears for an hour, the eggs most likely start to spawn 20 minutes prior to the time. + $diff = $starttime->diff($endtime); + if($diff->format('%h') == 1) { + $starttime->sub(new DateInterval('PT20M')); + } + + $date_start = $starttime->format('Y-m-d H:i:s'); + $date_end = $endtime->format('Y-m-d H:i:s'); + + $boss = $news['pokemon']; + $dex_id_form = resolve_boss_name_to_ids($boss); + + // Exit if resolving boss failed + if($dex_id_form[0] == 0) continue; + + // In case Pokebattler keeps using RAID_LEVEL_MEGA_5 (legendary mega tier) for primal raids + if(in_array($dex_id_form[0], PRIMAL_MONS) && $raid_level_id == 7) { + $raid_level_id = 10; + }elseif(in_array($dex_id_form[1], [-1,-2,-3]) && $raid_level_id == 7) { + $raid_level_id = 16; } - if($count > 0) $sql.=';'; - if($return_sql) return $sql; - else return $list; + if($prev_start != $date_start or $prev_end != $date_end) { + $list.= CR . EMOJI_CLOCK . ' ' . $starttime->format('j.n. ') . getTranslation('raid_egg_opens_at') . $starttime->format(' H:i') . ' — ' . $endtime->format('j.n. ') . getTranslation('raid_egg_opens_at') . $endtime->format(' H:i') . ':' . CR; + $prev_rl = ''; + } + if($prev_rl != $raid_level_id) { + $list.= '' . getTranslation($raid_level_id . 'stars') .':' . CR; + } + $list.= get_local_pokemon_name($dex_id_form[0], $dex_id_form[1]) . CR; + $prev_start = $date_start; + $prev_end = $date_end; + $prev_rl = $raid_level_id; + + if($count == 0) { + $count++; + $sql .= 'INSERT INTO raid_bosses (pokedex_id, pokemon_form_id, date_start, date_end, raid_level, scheduled) VALUES '; + $sql .= '("'.$dex_id_form[0].'","'.$dex_id_form[1].'","'.$date_start.'","'.$date_end.'","'.$raid_level_id.'", 1)'; + }else { + $sql .= ',("'.$dex_id_form[0].'","'.$dex_id_form[1].'","'.$date_start.'","'.$date_end.'","'.$raid_level_id.'", 1)'; + } + $returnArr[] = [ + 'pokedex_id' => $dex_id_form[0], + 'pokemon_form_id' => $dex_id_form[1], + 'date_start' => $date_start, + 'date_end' => $date_end, + 'raid_level' => $raid_level_id, + ]; + } + if($count > 0) $sql.=';'; + + if($returnFormat == 'sql') return $sql; + elseif($returnFormat == 'array') return $returnArr; + else return $list; } -?> \ No newline at end of file diff --git a/logic/resolvePokebattlerNameToIds.php b/logic/resolvePokebattlerNameToIds.php new file mode 100644 index 00000000..a5fb8ab3 --- /dev/null +++ b/logic/resolvePokebattlerNameToIds.php @@ -0,0 +1,51 @@ + [29, 776], + 'WURMPLE_NOEVOLVE_FORM' => [265, 600], + ]; + if(isset($hardCoded[$protoName])) return $hardCoded[$protoName]; + $data = createProtoNames(); + if(isset($data[$protoName])) return $data[$protoName]; + return false; +} + +function createProtoNames() { + static $result = []; + if($result !== []) return $result; + + $megaForms = ['','MEGA','MEGA_X','MEGA_Y','PRIMAL']; + $data = json_decode(curl_get_contents('https://raw.githubusercontent.com/WatWowMap/Masterfile-Generator/master/master-latest.json'), true); + foreach($data['pokemon'] as $pokemon) { + $protoNameBase = str_replace("♂","_MALE",str_replace("♀","_FEMALE",preg_replace("/\s/","_",strtoupper($pokemon['name'])))); + $formCount = count($pokemon['forms']); + $i = 1; + foreach($pokemon['forms'] as $formId => $form) { + if($formId == "0" && $formCount > 1) continue; + if($formId == (string)$pokemon['default_form_id']) { + $result[$protoNameBase] = [$pokemon['pokedex_id'], (int)$formId]; + $result[$form['proto'].'_FORM'] = [$pokemon['pokedex_id'], (int)$formId]; + }elseif($i == $formCount-1 && $pokemon['default_form_id'] == 0) { // Some random logic for Xerneas + $result[$protoNameBase] = [$pokemon['pokedex_id'], (int)$formId]; + }else { + if($form['proto'] == $protoNameBase.'_NORMAL') $protoName = $protoNameBase; + else $protoName = $form['proto'].'_FORM'; + $result[$protoName] = [$pokemon['pokedex_id'], (int)$formId]; + } + $i++; + } + if(!isset($pokemon['temp_evolutions'])) continue; + foreach($pokemon['temp_evolutions'] as $evoId => $evo) { + $result[$protoNameBase.'_'.$megaForms[(int)$evoId]] = [$pokemon['pokedex_id'], (int)$evoId]; + } + } + return $result; +} diff --git a/logic/resolve_boss_name_to_ids.php b/logic/resolve_boss_name_to_ids.php index e64f8ffe..25f2da5f 100644 --- a/logic/resolve_boss_name_to_ids.php +++ b/logic/resolve_boss_name_to_ids.php @@ -1,70 +1,26 @@ = 1) { - $pokemon = str_replace('_', '-', $pokemon_name); - } else { - $pokemon = $pokemon_name; - } - // Name and form. - $name = $pokemon; - $form = 'normal'; - - // Fix for GIRATINA as the actual GIRATINA_ALTERED_FORM is just GIRATINA - if($name == 'GIRATINA' && $form == 'normal') { - $form = 'ALTERED'; - } - } - // Get ID and form name used internally. - debug_log('Getting dex id and form for pokemon ' . $name . ' with form ' . $form); - return get_pokemon_id_by_name($name . ' ' . $form, true); + global $pokebattler_pokemon_map; + if (array_key_exists($pokemon_name, $pokebattler_pokemon_map)) + $pokemon_name = $pokebattler_pokemon_map[$pokemon_name]; + // Name and form. + $name = $pokemon_name; + $form = 'normal'; + // Pokemon name ending with "_FORM" ? + if (preg_match('/(MEGA|MEGA_Y|MEGA_X|PRIMAL|FORM|SHADOW|FORME)$/', $pokemon_name)) { + debug_log('Pokemon with a special form received: ' . $pokemon_name); + // Remove "_FORM" + $pokemon = preg_replace('/_FORM$/', '', $pokemon_name); + + // Get pokemon name and form. + [$name, $form] = explode("_", $pokemon, 2); + + // Pokebattler uses different forms for shadow pokemon so we'll just convert those to normal + if($form == 'SHADOW') $form = 'normal'; + } + // Get ID and form name used internally. + debug_log('Getting dex id and form for pokemon ' . $name . ' with form ' . $form); + return get_pokemon_id_by_name($name, $form, true); } - -?> \ No newline at end of file diff --git a/logic/resolve_raid_boss.php b/logic/resolve_raid_boss.php index 405dfaea..335ec78c 100644 --- a/logic/resolve_raid_boss.php +++ b/logic/resolve_raid_boss.php @@ -8,37 +8,38 @@ * @return array */ function resolve_raid_boss($pokemon, $pokemon_form, $spawn, $raid_level) { - if($pokemon == 0) { - $tz_diff = tz_diff(); - $query = my_query(' SELECT pokedex_id, pokemon_form_id - FROM raid_bosses - WHERE raid_level = "' . $raid_level . '" - AND scheduled = 1 - AND convert_tz("' . $spawn . '","+00:00","'.$tz_diff.'") BETWEEN date_start AND date_end'); - if($query->rowCount() == 1) { - $row = $query->fetch(); - // Return active boss - $pokemon_id = $row['pokedex_id']; - $pokemon_form_id = $row['pokemon_form_id']; - }else { - // Return egg - $pokemon_id = '999'.$raid_level; - $pokemon_form_id = 0; - } - }else { - $pokemon_id = $pokemon; - if($pokemon_form == 0) { - // If pokemon_form is 0 (often received from webhook), resolve the form id of normal form from our database - $form_query = my_query(' SELECT pokemon_form_id - FROM pokemon - WHERE pokedex_id = "' . $pokemon . '" - AND pokemon_form_name = \'normal\' - LIMIT 1'); - $pokemon_form_id = $form_query->fetch()['pokemon_form_id']; - }else { - $pokemon_form_id = $pokemon_form; - } + if($pokemon == 0) { + $tz_diff = tz_diff(); + $query = my_query(' + SELECT DISTINCT pokedex_id, pokemon_form_id + FROM raid_bosses + WHERE raid_level = :raidLevel + AND scheduled = 1 + AND disabled = 0 + AND convert_tz(:spawn, "+00:00", :tzDiff) BETWEEN date_start AND date_end + ', ['raidLevel' => $raid_level, 'spawn' => $spawn, 'tzDiff' => $tz_diff]); + // Return egg + $pokemon_id = '999'.$raid_level; + $pokemon_form_id = 0; + if($query->rowCount() == 1) { + $row = $query->fetch(); + // Return active boss + $pokemon_id = $row['pokedex_id']; + $pokemon_form_id = $row['pokemon_form_id']; } return ['pokedex_id' => $pokemon_id, 'pokemon_form_id' => $pokemon_form_id]; + } + $pokemon_id = $pokemon; + $pokemon_form_id = $pokemon_form; + if($pokemon_form == 0) { + // If pokemon_form is 0 (often received from webhook), resolve the form id of normal form from our database + $form_query = my_query(' + SELECT pokemon_form_id + FROM pokemon + WHERE pokedex_id = ? + AND pokemon_form_name = \'normal\' + LIMIT 1', [$pokemon]); + $pokemon_form_id = $form_query->fetch()['pokemon_form_id']; + } + return ['pokedex_id' => $pokemon_id, 'pokemon_form_id' => $pokemon_form_id]; } -?> diff --git a/logic/send_raid_poll.php b/logic/send_raid_poll.php index c6dd53fb..4106ef1e 100644 --- a/logic/send_raid_poll.php +++ b/logic/send_raid_poll.php @@ -1,78 +1,92 @@ $raid_id, + ]); + $chatsAlreadySharedTo = $resultChats->fetchAll(PDO::FETCH_COLUMN, 0); - // Get raid data. - if($raid == false) $raid = get_raid($raid_id); + // Get raid data. + if($raid == false) $raid = get_raid($raid_id); - // Get text and keys. - $text = show_raid_poll($raid); - $keys = keys_vote($raid); + // Get text and keys. + $text = show_raid_poll($raid); + $keys = keys_vote($raid); - $post_text = false; - if(array_key_exists('short', $text)) { - $msg_short_len = strlen(utf8_decode($text['short'])); - debug_log($msg_short_len, 'Raid poll short message length:'); - // Message short enough? - if($msg_short_len >= 1024) { - // Use full text and reset text to true regardless of prior value - $post_text = true; - } - } else { - // Use full text and reset text to true regardless of prior value - $post_text = true; + $post_text = false; + if(array_key_exists('short', $text)) { + $msg_short_len = strlen(mb_convert_encoding($text['short'], 'ISO-8859-1')); + debug_log($msg_short_len, 'Raid poll short message length:'); + // Message short enough? + if($msg_short_len >= 1024) { + // Use full text and reset text to true regardless of prior value + $post_text = true; } + } else { + // Use full text and reset text to true regardless of prior value + $post_text = true; + } - // Telegram JSON array. - if($tg_json == false) $tg_json = []; + // Send the message. + $raid_picture_hide_level = explode(",",$config->RAID_PICTURE_HIDE_LEVEL); + $raid_picture_hide_pokemon = explode(",",$config->RAID_PICTURE_HIDE_POKEMON); + $raid_poll_hide_buttons_levels = explode(",",$config->RAID_POLL_HIDE_BUTTONS_RAID_LEVEL); - // Raid picture - if($config->RAID_PICTURE) { - require_once(LOGIC_PATH . '/raid_picture.php'); - $picture_url = raid_picture_url($raid, $config->RAID_PICTURE_AUTOEXTEND); - } - - // Send the message. - $raid_picture_hide_level = explode(",",$config->RAID_PICTURE_HIDE_LEVEL); - $raid_picture_hide_pokemon = explode(",",$config->RAID_PICTURE_HIDE_POKEMON); - $raid_poll_hide_buttons_levels = explode(",",$config->RAID_POLL_HIDE_BUTTONS_RAID_LEVEL); - - $raid_pokemon_id = $raid['pokemon']; - $raid_level = $raid['level']; - $raid_pokemon_form_name = get_pokemon_form_name($raid_pokemon_id,$raid['pokemon_form']); - $raid_pokemon = $raid_pokemon_id . "-" . $raid_pokemon_form_name; + $raid_pokemon_id = $raid['pokemon']; + $raid_level = $raid['level']; + $raid_pokemon_form_name = get_pokemon_form_name($raid_pokemon_id,$raid['pokemon_form']); + $raid_pokemon = $raid_pokemon_id . "-" . $raid_pokemon_form_name; - if(!is_array($chats)) $chats = [$chats]; - foreach($chats as $chat_id) { - // Send location. - if ($config->RAID_LOCATION) { - // Send location. - $msg_text = !empty($raid['address']) ? $raid['address'] : $raid['pokemon']; - // Sending venue together with raid poll can't be multicurled since they would appear to the chat in random order - send_venue($chat_id, $raid['lat'], $raid['lon'], '', $msg_text, false, false, $raid_id); - send_message($chat_id, $text['full'], $keys, ['disable_web_page_preview' => 'true'], false, $raid_id); - }else { - if($config->RAID_PICTURE && $raid['event_hide_raid_picture'] == 0 && !in_array($raid_level, $raid_picture_hide_level) && !in_array($raid_pokemon, $raid_picture_hide_pokemon) && !in_array($raid_pokemon_id, $raid_picture_hide_pokemon)) { - if(($config->RAID_PICTURE_AUTOEXTEND && !in_array($raid['level'], $raid_poll_hide_buttons_levels)) or $post_text) { - send_photo($chat_id, $picture_url, '', [], [], false, $raid_id); - send_message($chat_id, $text['short'], $keys, ['disable_web_page_preview' => 'true'], false, $raid_id); - } else { - $tg_json[] = send_photo($chat_id, $picture_url, $text['short'], $keys, ['disable_web_page_preview' => 'true'], true, $raid_id); - } - } else { - $tg_json[] = send_message($chat_id, $text['full'], $keys, ['disable_web_page_preview' => 'true'], true, $raid_id); - } - } + foreach($shareChatObjs as $chatObj) { + // Check if Raid has been posted to target chat + if(in_array($chatObj['id'], $chatsAlreadySharedTo)) continue; + if ($config->RAID_LOCATION) { + // Send location. + $msg_text = !empty($raid['address']) ? $raid['address'] : $raid['pokemon']; + // Sending venue together with raid poll can't be multicurled since they would appear to the chat in random order + send_venue($chatObj, $raid['lat'], $raid['lon'], '', $msg_text, false, false, $raid_id); + send_message($chatObj, $text['full'], $keys, ['disable_web_page_preview' => 'true'], false, $raid_id); + continue; + } + if(!$config->RAID_PICTURE || $raid['event_hide_raid_picture'] == 1 || in_array($raid_level, $raid_picture_hide_level) || in_array($raid_pokemon, $raid_picture_hide_pokemon) || in_array($raid_pokemon_id, $raid_picture_hide_pokemon)) { + $tg_json[] = send_message($chatObj, $text['full'], $keys, ['disable_web_page_preview' => 'true'], true, $raid_id); + continue; + } + require_once(LOGIC_PATH . '/raid_picture.php'); + if(!($config->RAID_PICTURE_AUTOEXTEND && !in_array($raid['level'], $raid_poll_hide_buttons_levels)) && $post_text == false) { + $media_content = get_raid_picture($raid); + $tg_json[] = send_photo($chatObj, $media_content[1], $media_content[0], $text['short'], $keys, ['disable_web_page_preview' => 'true'], true, $raid); + continue; } - return $tg_json; + $media_content = get_raid_picture($raid, true); + $raid['standalone_photo'] = true; // Inject this into raid array so we can pass it all the way to photo cache + send_photo($chatObj, $media_content[1], $media_content[0], '', [], [], false, $raid); + send_message($chatObj, $text['short'], $keys, ['disable_web_page_preview' => 'true'], false, $raid_id); + } + return $tg_json; } -?> \ No newline at end of file diff --git a/logic/send_trainerinfo.php b/logic/send_trainerinfo.php index 5ac1eab4..0fa43aff 100644 --- a/logic/send_trainerinfo.php +++ b/logic/send_trainerinfo.php @@ -1,35 +1,35 @@ 'true'], true); + // Edit the message. + $tg_json[] = edit_message($update, $msg, $keys, ['disable_web_page_preview' => 'true'], true); - // Telegram multicurl request. - curl_json_multi_request($tg_json); + // Telegram multicurl request. + curl_json_multi_request($tg_json); - // Exit. - exit(); + // Exit. + exit(); } - -?> diff --git a/logic/send_vote_remote_users_limit_reached.php b/logic/send_vote_remote_users_limit_reached.php index a96ef971..a4cbb59e 100644 --- a/logic/send_vote_remote_users_limit_reached.php +++ b/logic/send_vote_remote_users_limit_reached.php @@ -5,11 +5,9 @@ */ function send_vote_remote_users_limit_reached($update) { - // Set the message. - $msg = getTranslation('vote_remote_users_limit_reached'); + // Set the message. + $msg = getTranslation('vote_remote_users_limit_reached'); - // Answer the callback. - answerCallbackQuery($update['callback_query']['id'], $msg); + // Answer the callback. + answerCallbackQuery($update['callback_query']['id'], $msg); } - -?> diff --git a/logic/send_vote_time_first.php b/logic/send_vote_time_first.php index b6305545..fbc69e19 100644 --- a/logic/send_vote_time_first.php +++ b/logic/send_vote_time_first.php @@ -5,13 +5,11 @@ */ function send_vote_time_first($update) { - // Set the message. - $msg = getTranslation('vote_time_first'); + // Set the message. + $msg = getTranslation('vote_time_first'); - // Answer the callback. - answerCallbackQuery($update['callback_query']['id'], $msg); + // Answer the callback. + answerCallbackQuery($update['callback_query']['id'], $msg); - exit(); + exit(); } - -?> diff --git a/logic/send_vote_time_future.php b/logic/send_vote_time_future.php index 21a17891..e11d60f8 100644 --- a/logic/send_vote_time_future.php +++ b/logic/send_vote_time_future.php @@ -5,11 +5,9 @@ */ function send_vote_time_future($update) { - // Set the message. - $msg = getPublicTranslation('vote_time_future'); + // Set the message. + $msg = getPublicTranslation('vote_time_future'); - // Answer the callback. - answerCallbackQuery($update['callback_query']['id'], $msg); + // Answer the callback. + answerCallbackQuery($update['callback_query']['id'], $msg); } - -?> diff --git a/logic/sendalarmnotice.php b/logic/sendalarmnotice.php index 924d3c30..cd263f6b 100644 --- a/logic/sendalarmnotice.php +++ b/logic/sendalarmnotice.php @@ -1,51 +1,48 @@ fetch(); - } - $gymname = '' . $raid['gym_name'] . ''; - // parse raidtimes - $raidtimes = str_replace(CR, '', str_replace(' ', '', get_raid_times($raid, false, true))); +function sendAlertOnOffNotice($raid_id, $user_id, $alarm = null, $raid = null) { + global $botUser; + if(empty($raid)){ + // Request limited raid info + $request = my_query(' + SELECT g.gym_name, r.start_time, r.end_time + FROM raids as r + LEFT JOIN gyms as g + ON r.gym_id = g.id + WHERE r.id = ? + ', [$raid_id]); + $raid = $request->fetch(); + } + $gymname = '' . $raid['gym_name'] . ''; + // parse raidtimes + $raidtimes = str_replace(CR, '', str_replace(' ', '', get_raid_times($raid, $botUser->userLanguage, true))); - if(empty($alarm)){ - // Get the new value - $rs = my_query( - " - SELECT alarm - FROM attendance - WHERE raid_id = {$raid_id} - AND user_id = {$user_id} - " - ); - $answer = $rs->fetch(); - $alarm = $answer['alarm']; - } + if(empty($alarm)){ + // Get the new value + $rs = my_query(' + SELECT alarm + FROM attendance + WHERE raid_id = ? + AND user_id = ? + ',[$raid_id, $user_id] + ); + $answer = $rs->fetch(); + $alarm = $answer['alarm']; + } - $msg_text = ''; - - if($alarm) {// Enable alerts message. - $msg_text = EMOJI_ALARM . SP . '' . getTranslation('alert_updates_on') . '' . CR; - } else {// Disable alerts message. - $msg_text = EMOJI_NO_ALARM . SP . '' . getTranslation('alert_no_updates') . '' . CR; + if($alarm) {// Enable alerts message. + $msg_text = EMOJI_ALARM . SP . '' . getTranslation('alert_updates_on') . '' . CR; + } else {// Disable alerts message. + $msg_text = EMOJI_NO_ALARM . SP . '' . getTranslation('alert_no_updates') . '' . CR; } - $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')'; - send_message($user_id, $msg_text); + $msg_text .= EMOJI_HERE . SP . $gymname . SP . '(' . $raidtimes . ')'; + send_message(create_chat_object([$user_id]), $msg_text); } - -?> diff --git a/logic/show_raid_poll.php b/logic/show_raid_poll.php index f8c15cae..26ace6aa 100644 --- a/logic/show_raid_poll.php +++ b/logic/show_raid_poll.php @@ -1,4 +1,12 @@ ".$raid['event_name']."".CR, true); - } + global $config; - // Get raid times. - $msg = raid_poll_message($msg, get_raid_times($raid), true); + $msg = array(); - // Get current time and time left. - $time_now = utcnow(); - $time_left = $raid['t_left']; + // Get current pokemon + $raid_pokemon_id = $raid['pokemon']; + $raid_pokemon_form_id = $raid['pokemon_form']; + $raid_pokemon_form_name = ($raid_pokemon_form_id != 0) ? get_pokemon_form_name($raid_pokemon_id, $raid_pokemon_form_id) : ''; + $raid_pokemon = $raid_pokemon_id . "-" . $raid_pokemon_form_id; + $raid_pokemon_info = get_pokemon_info($raid_pokemon_id, $raid_pokemon_form_id); - // Display gym details. - if ($raid['gym_name'] || $raid['gym_team']) { - // Add gym name to message. - if ($raid['gym_name']) { - $ex_raid_gym_marker = (strtolower($config->RAID_EX_GYM_MARKER) == 'icon') ? EMOJI_STAR : '' . $config->RAID_EX_GYM_MARKER . ''; - $msg = raid_poll_message($msg, getPublicTranslation('gym') . ': ' . ($raid['ex_gym'] ? $ex_raid_gym_marker . SP : '') . '' . $raid['gym_name'] . '', true); - } + // Get raid level + $raid_level = $raid['level']; - // Add team to message. - if ($raid['gym_team']) { - $msg = raid_poll_message($msg, SP . $GLOBALS['teams'][$raid['gym_team']], true); - } + // Are remote players allowed for this raid? + $raid_local_only = in_array($raid_level, RAID_LEVEL_LOCAL_ONLY); + + if($raid['event_name'] != NULL && $raid['event_name'] != "") { + $msg = raid_poll_message($msg, "".$raid['event_name']."".CR, true); + } + + // Get raid times. + $msg = raid_poll_message($msg, get_raid_times($raid, $config->LANGUAGE_PUBLIC, ($raid['event_pokemon_title'] == 0 ? true : false)), true); + + // Get current time and time left. + $time_now = utcnow(); + $time_left = $raid['t_left']; + + // Display gym details. + if ($raid['gym_name'] || $raid['gym_team']) { + // Add gym name to message. + if ($raid['gym_name']) { + $ex_raid_gym_marker = (strtolower($config->RAID_EX_GYM_MARKER) == 'icon') ? EMOJI_STAR : '' . $config->RAID_EX_GYM_MARKER . ''; + $msg = raid_poll_message($msg, getPublicTranslation('gym') . ': ' . ($raid['ex_gym'] ? $ex_raid_gym_marker . SP : '') . '' . $raid['gym_name'] . '', true); + } - $msg = raid_poll_message($msg, CR, true); + // Add team to message. + if ($raid['gym_team']) { + $msg = raid_poll_message($msg, SP . $GLOBALS['teams'][$raid['gym_team']], true); } - // Add maps link to message. - if (!empty($raid['address'])) { - $msg = raid_poll_message($msg, ($config->RAID_PICTURE ? $raid['gym_name'].': ' : ''). mapslink($raid) . CR); + $msg = raid_poll_message($msg, CR, true); + } + + // Add maps link to message. + if (!empty($raid['address'])) { + $msg = raid_poll_message($msg, ($config->RAID_PICTURE ? $raid['gym_name'].': ' : ''). mapslink($raid) . CR); + } else { + // Get the address. + $addr = get_address($raid['lat'], $raid['lon']); + $address = format_address($addr); + + //Only store address if not empty + if(!empty($address)) { + my_query(' + UPDATE gyms + SET address = ? + WHERE id = ? + ', [$address, $raid['gym_id']] + ); + //Use new address + $msg = raid_poll_message($msg, ($config->RAID_PICTURE ? $raid['gym_name'].': ' : ''). mapslink($raid,$address) . CR); } else { - // Get the address. - $addr = get_address($raid['lat'], $raid['lon']); - $address = format_address($addr); - - //Only store address if not empty - if(!empty($address)) { - my_query( - " - UPDATE gyms - SET address = ? - WHERE id = '{$raid['gym_id']}' - ", - [$address] - ); - //Use new address - $msg = raid_poll_message($msg, ($config->RAID_PICTURE ? $raid['gym_name'].': ' : ''). mapslink($raid,$address) . CR); - } else { - //If no address is found show maps link - $msg = raid_poll_message($msg, ($config->RAID_PICTURE ? $raid['gym_name'].': ' : ''). mapslink($raid,'1') . CR); - } + //If no address is found show maps link + $msg = raid_poll_message($msg, ($config->RAID_PICTURE ? $raid['gym_name'].': ' : ''). mapslink($raid,'1') . CR); } + } + // Display raid boss name and boss' weather unless hidden for a specific event + if($raid['event_pokemon_title'] != 0 or $raid['event_pokemon_title'] == NULL) { // Display raid boss name. - $msg = raid_poll_message($msg, getPublicTranslation('raid_boss') . ': ' . get_local_pokemon_name($raid_pokemon_id, $raid['pokemon_form'], true) . '', true); + if($raid['event_pokemon_title'] == 1) $title = getPublicTranslation('raid_boss'); + elseif($raid['event_pokemon_title'] == 2) $title = getPublicTranslation('featured_pokemon'); + else $title = getPublicTranslation('raid_boss'); + $msg = raid_poll_message($msg, $title . ': ' . get_local_pokemon_name($raid_pokemon_id, $raid['pokemon_form'], $config->LANGUAGE_PUBLIC) . ' ' . (isset($raid['shadow']) && $raid['shadow'] && !in_array($raid['pokemon'], EGGS) ? ' ' . getPublicTranslation('pokemon_form_shadow') : '') . '', true); // Display raid boss weather. - $pokemon_weather = get_pokemon_weather($raid_pokemon_id, $raid_pokemon_form_id); - $msg = raid_poll_message($msg, ($pokemon_weather != 0) ? (' ' . get_weather_icons($pokemon_weather)) : '', true); + $msg = raid_poll_message($msg, ($raid_pokemon_info['weather'] != 0) ? (' ' . get_weather_icons($raid_pokemon_info['weather'])) : '', true); $msg = raid_poll_message($msg, CR, true); + } - // Display attacks. - if ($raid['move1'] > 1 && $raid['move2'] > 2 ) { - $msg = raid_poll_message($msg, getPublicTranslation('pokemon_move_' . $raid['move1']) . '/' . getPublicTranslation('pokemon_move_' . $raid['move2'])); - $msg = raid_poll_message($msg, CR); - } + // Display attacks. + if ($raid['move1'] > 1 && $raid['move2'] > 2 ) { + $msg = raid_poll_message($msg, getPublicTranslation('pokemon_move_' . $raid['move1']) . '/' . getPublicTranslation('pokemon_move_' . $raid['move2'])); + $msg = raid_poll_message($msg, CR); + } - // Hide participants? - if($config->RAID_POLL_HIDE_USERS_TIME > 0) { - if($config->RAID_ANYTIME) { - $hide_users_sql = "AND (attend_time > (UTC_TIMESTAMP() - INTERVAL " . $config->RAID_POLL_HIDE_USERS_TIME . " MINUTE) OR attend_time = '". ANYTIME ."')"; - } else { - $hide_users_sql = "AND attend_time > (UTC_TIMESTAMP() - INTERVAL " . $config->RAID_POLL_HIDE_USERS_TIME . " MINUTE)"; - } + // Hide participants? + $hide_users_sql = ""; + if($config->RAID_POLL_HIDE_USERS_TIME > 0) { + if($config->RAID_ANYTIME) { + $hide_users_sql = 'AND (attend_time > (UTC_TIMESTAMP() - INTERVAL ' . $config->RAID_POLL_HIDE_USERS_TIME . ' MINUTE) OR attend_time = \''. ANYTIME .'\')'; } else { - $hide_users_sql = ""; + $hide_users_sql = 'AND attend_time > (UTC_TIMESTAMP() - INTERVAL ' . $config->RAID_POLL_HIDE_USERS_TIME . ' MINUTE)'; } + } + + // When the raid egg is hatched, hide all attendances that did not voted for hatched pokemon or any pokemon + // Remaining attendances are combined and treated as users that voted for any pokemon from now on + $order_by_sql = 'pokemon,'; + $combine_attendances = false; + if(!in_array($raid['pokemon'], EGGS)) { + $hide_users_sql.= 'AND (pokemon = \''.$raid['pokemon'].'-'.$raid['pokemon_form'].'\' OR pokemon = \'0\')'; + $order_by_sql = ''; // Remove sorting by pokemon since all attendances are combined + $combine_attendances = true; + } + + // Buttons for raid levels and pokemon hidden? + $hide_buttons_raid_level = explode(',', $config->RAID_POLL_HIDE_BUTTONS_RAID_LEVEL); + $hide_buttons_pokemon = explode(',', $config->RAID_POLL_HIDE_BUTTONS_POKEMON); + $buttons_hidden = false; + if(in_array($raid_level, $hide_buttons_raid_level) || in_array($raid_pokemon_id, $hide_buttons_pokemon) || in_array($raid_pokemon_id.'-'.$raid_pokemon_form_name, $hide_buttons_pokemon)) { + $buttons_hidden = true; + } + + // Get attendances + $rs_attendance = my_query(' + SELECT attendance.*, + users.name, + users.nick, + users.trainername, + users.display_name, + users.trainercode, + users.level, + users.team + FROM attendance + LEFT JOIN users + ON attendance.user_id = users.user_id + WHERE raid_id = ? + ' . $hide_users_sql . ' + AND attend_time IS NOT NULL + ORDER BY attend_time, + ' . $order_by_sql . ' + can_invite DESC, + users.team, + arrived, + users.level desc, + users.name + ', [$raid['id']] + ); + + // Init empty attendance array and trigger variables + $att_array = $cnt_array = $done_array = $cancel_array = []; + $cnt_all = $cnt_remote = $cnt_want_invite = $cnt_extra_alien = $cnt_latewait = $cnt_cancel = $cnt_done = $cnt_can_invite = 0; + + while ($attendance = $rs_attendance->fetch()) { + // Attendance found + $cnt_all = 1; + $attendance_pokemon = $combine_attendances ? 0 : $attendance['pokemon']; // If raid egg has hatched, combine all attendances under 'any pokemon' + + // Check trainername + $attendance = check_trainername($attendance); + + // Define variables if necessary + if(!isset($cnt_array[$attendance['attend_time']][$attendance_pokemon])) + $cnt_array[$attendance['attend_time']][$attendance_pokemon]['extra_alien']=$cnt_array[$attendance['attend_time']][$attendance_pokemon]['in_person']=$cnt_array[$attendance['attend_time']][$attendance_pokemon]['late']=$cnt_array[$attendance['attend_time']][$attendance_pokemon]['remote']=$cnt_array[$attendance['attend_time']][$attendance_pokemon]['want_invite']=$cnt_array[$attendance['attend_time']][$attendance_pokemon]['total']=0; + if(!isset($cnt_array[$attendance['attend_time']]['other_pokemon'])) $cnt_array[$attendance['attend_time']]['other_pokemon'] = $cnt_array[$attendance['attend_time']]['raid_pokemon'] = $cnt_array[$attendance['attend_time']]['any_pokemon'] = 0; + + if($attendance['cancel'] == 0 && $attendance['raid_done'] == 0 && $attendance['can_invite'] == 0) { + // These counts are used to control printing of pokemon/time headers, so just number of entries is enough + if($attendance['pokemon'] != 0 && $raid_pokemon != $attendance['pokemon']){ + $cnt_array[$attendance['attend_time']]['other_pokemon']+=1; + }elseif($attendance['pokemon'] != 0 && $raid_pokemon == $attendance['pokemon']) { + $cnt_array[$attendance['attend_time']]['raid_pokemon']+= 1; + }else { + $cnt_array[$attendance['attend_time']]['any_pokemon']+= 1; + } + + // Adding to total count of specific pokemon at specific time + $cnt_array[$attendance['attend_time']][$attendance_pokemon]['total'] += 1 + $attendance['extra_in_person'] + $attendance['extra_alien']; + + if($attendance['want_invite'] == 0) { + // Fill attendance array with results + $att_array[$attendance['attend_time']][$attendance_pokemon][] = $attendance; - // When the raid egg is hatched, hide all attendances that did not voted for hatched pokemon or any pokemon - // Remaining attendances are combined and treated as users that voted for any pokemon from now on - $order_by_sql = 'pokemon,'; - $combine_attendances = false; - if(!in_array($raid['pokemon'], $GLOBALS['eggs'])) { - $hide_users_sql.= 'AND (pokemon = \''.$raid['pokemon'].'-'.$raid['pokemon_form'].'\' OR pokemon = \'0\')'; - $order_by_sql = ''; // Remove sorting by pokemon since all attendances are combined - $combine_attendances = true; - } - - // Buttons for raid levels and pokemon hidden? - $hide_buttons_raid_level = explode(',', $config->RAID_POLL_HIDE_BUTTONS_RAID_LEVEL); - $hide_buttons_pokemon = explode(',', $config->RAID_POLL_HIDE_BUTTONS_POKEMON); - $buttons_hidden = false; - if(in_array($raid_level, $hide_buttons_raid_level) || in_array($raid_pokemon_id, $hide_buttons_pokemon) || in_array($raid_pokemon_id.'-'.$raid_pokemon_form_name, $hide_buttons_pokemon)) { - $buttons_hidden = true; - } - - // Get attendances - $rs_attendance = my_query( - " - SELECT attendance.*, - users.name, - users.nick, - users.trainername, - users.display_name, - users.trainercode, - users.level, - users.team - FROM attendance - LEFT JOIN users - ON attendance.user_id = users.user_id - WHERE raid_id = {$raid['id']} - {$hide_users_sql} - AND attend_time IS NOT NULL - ORDER BY attend_time, - {$order_by_sql} - can_invite DESC, - users.team, - arrived, - users.level desc, - users.name - " - ); - - // Init empty attendance array and trigger variables - $att_array = []; - $cnt_array = []; - $done_array = []; - $cancel_array = []; - $cnt_all = 0; - $cnt_remote = 0; - $cnt_want_invite = 0; - $cnt_extra_alien = 0; - $cnt_latewait = 0; - $cnt_cancel = 0; - $cnt_done = 0; - $cnt_can_invite = 0; - - while ($attendance = $rs_attendance->fetch()) { - // Attendance found - $cnt_all = 1; - $attendance_pokemon = $combine_attendances ? 0 : $attendance['pokemon']; // If raid egg has hatched, combine all attendances under 'any pokemon' - - // Check trainername - $attendance = check_trainername($attendance); - - // Define variables if necessary - if(!isset($cnt_array[$attendance['attend_time']][$attendance_pokemon])) - $cnt_array[$attendance['attend_time']][$attendance_pokemon]['extra_alien']=$cnt_array[$attendance['attend_time']][$attendance_pokemon]['in_person']=$cnt_array[$attendance['attend_time']][$attendance_pokemon]['late']=$cnt_array[$attendance['attend_time']][$attendance_pokemon]['remote']=$cnt_array[$attendance['attend_time']][$attendance_pokemon]['want_invite']=$cnt_array[$attendance['attend_time']][$attendance_pokemon]['total']=0; - if(!isset($cnt_array[$attendance['attend_time']]['other_pokemon'])) $cnt_array[$attendance['attend_time']]['other_pokemon'] = $cnt_array[$attendance['attend_time']]['raid_pokemon'] = $cnt_array[$attendance['attend_time']]['any_pokemon'] = 0; - - if($attendance['cancel'] == 0 && $attendance['raid_done'] == 0 && $attendance['can_invite'] == 0) { - // These counts are used to control printing of pokemon/time headers, so just number of entries is enough - if($attendance['pokemon'] != 0 && $raid_pokemon != $attendance['pokemon']){ - $cnt_array[$attendance['attend_time']]['other_pokemon']+=1; - }elseif($attendance['pokemon'] != 0 && $raid_pokemon == $attendance['pokemon']) { - $cnt_array[$attendance['attend_time']]['raid_pokemon']+= 1; - }else { - $cnt_array[$attendance['attend_time']]['any_pokemon']+= 1; - } - - // Adding to total count of specific pokemon at specific time - $cnt_array[$attendance['attend_time']][$attendance_pokemon]['total'] += 1 + $attendance['extra_in_person'] + $attendance['extra_alien']; - - if($attendance['want_invite'] == 0) { - // Fill attendance array with results - $att_array[$attendance['attend_time']][$attendance_pokemon][] = $attendance; - - // Fill counts array - if($attendance['extra_alien'] > 0) { - $cnt_extra_alien = 1; - $cnt_array[$attendance['attend_time']][$attendance_pokemon]['extra_alien'] += $attendance['extra_alien']; - } - - if($attendance['late'] == 1) { - $cnt_latewait = 1; - $cnt_array[$attendance['attend_time']][$attendance_pokemon]['late'] += 1 + $attendance['extra_in_person'] + $attendance['extra_alien']; - } - if($attendance['remote'] == 1) { - $cnt_remote = 1; - $cnt_array[$attendance['attend_time']][$attendance_pokemon]['remote'] += 1 + $attendance['extra_in_person']; - }else { - $cnt_array[$attendance['attend_time']][$attendance_pokemon]['in_person'] += 1 + $attendance['extra_in_person']; - } - }else if($attendance['want_invite'] == 1) { - // Create array key for attend time and pokemon to maintain correct sorting order - if(!array_key_exists($attendance['attend_time'], $att_array)) { - $att_array[$attendance['attend_time']] = []; - $att_array[$attendance['attend_time']][$attendance_pokemon] = []; - }elseif(!array_key_exists($attendance_pokemon, $att_array[$attendance['attend_time']])) { - $att_array[$attendance['attend_time']][$attendance_pokemon] = []; - } - - $cnt_array[$attendance['attend_time']][$attendance_pokemon]['want_invite'] += 1 + $attendance['extra_in_person']; - } - }else { - if($attendance['raid_done']==1) { - $cnt_done += 1 + $attendance['extra_in_person'] + $attendance['extra_alien']; - $done_array[$attendance['user_id']] = $attendance; // Adding user_id as key to overwrite duplicate entries and thus only display user once even if they made multiple pokemon selections - }else if($attendance['cancel']==1) { - $cnt_cancel += 1 + $attendance['extra_in_person'] + $attendance['extra_alien']; - $cancel_array[$attendance['user_id']] = $attendance; - }else if($attendance['can_invite']==1) { - $cnt_can_invite++; - $att_array[$attendance['attend_time']][$attendance_pokemon][] = $attendance; - } + // Fill counts array + if($attendance['extra_alien'] > 0) { + $cnt_extra_alien = 1; + $cnt_array[$attendance['attend_time']][$attendance_pokemon]['extra_alien'] += $attendance['extra_alien']; } - } - // Get attendances with invite beggars - // Using a separate query so they can be displayed in the order they're in the db (who voted first) - $rs_attendance_want_inv = my_query( - " - SELECT attendance.*, - users.name, - users.nick, - users.trainername, - users.display_name, - users.trainercode, - users.level, - users.team - FROM attendance - LEFT JOIN users - ON attendance.user_id = users.user_id - WHERE raid_id = {$raid['id']} - AND want_invite = 1 - AND cancel = 0 - AND raid_done = 0 - {$hide_users_sql} - AND attend_time IS NOT NULL - ORDER BY attend_time, - {$order_by_sql} - attendance.id - " - ); - while ($attendance = $rs_attendance_want_inv->fetch()) { - // Attendance found - $cnt_want_invite = 1; - $attendance = check_trainername($attendance); - - // Fill attendance array with results - if($combine_attendances) { - $att_array[$attendance['attend_time']][0][] = $attendance; - }else { - $att_array[$attendance['attend_time']][$attendance['pokemon']][] = $attendance; + if($attendance['late'] == 1) { + $cnt_latewait = 1; + $cnt_array[$attendance['attend_time']][$attendance_pokemon]['late'] += 1 + $attendance['extra_in_person'] + $attendance['extra_alien']; } - } - // Raid has started and has participants - if($time_now > $raid['start_time']) { - // Add raid is done message. - if($time_now > $raid['end_time']) { - $msg = raid_poll_message($msg, '' . getPublicTranslation('raid_done') . '' . CR); - // Add time left message. - } else { - $msg = raid_poll_message($msg, getPublicTranslation('raid') . ' — ' . getPublicTranslation('still') . ' ' . $time_left . 'h' . CR); + if($attendance['remote'] == 1) { + $cnt_remote = 1; + $cnt_array[$attendance['attend_time']][$attendance_pokemon]['remote'] += 1 + $attendance['extra_in_person']; + }else { + $cnt_array[$attendance['attend_time']][$attendance_pokemon]['in_person'] += 1 + $attendance['extra_in_person']; } - if($cnt_all > 0 || $buttons_hidden) { - // Display raid boss CP values. - $pokemon_cp = get_formatted_pokemon_cp($raid_pokemon_id, $raid_pokemon_form_id, true); - $msg = raid_poll_message($msg, (!empty($pokemon_cp)) ? ($pokemon_cp . CR) : '', true); + }else if($attendance['want_invite'] == 1) { + // Create array key for attend time and pokemon to maintain correct sorting order + if(!array_key_exists($attendance['attend_time'], $att_array)) { + $att_array[$attendance['attend_time']] = []; + $att_array[$attendance['attend_time']][$attendance_pokemon] = []; + }elseif(!array_key_exists($attendance_pokemon, $att_array[$attendance['attend_time']])) { + $att_array[$attendance['attend_time']][$attendance_pokemon] = []; } - } - // Hide info if buttons are hidden - if($buttons_hidden) { - // Show message that voting is not possible! - $msg = raid_poll_message($msg, CR . '' . getPublicTranslation('raid_info_no_voting') . ' ' . CR); + $cnt_array[$attendance['attend_time']][$attendance_pokemon]['want_invite'] += 1 + $attendance['extra_in_person']; + } + }else { + if($attendance['raid_done']==1) { + $cnt_done += 1 + $attendance['extra_in_person'] + $attendance['extra_alien']; + $done_array[$attendance['user_id']] = $attendance; // Adding user_id as key to overwrite duplicate entries and thus only display user once even if they made multiple pokemon selections + }else if($attendance['cancel']==1) { + $cnt_cancel += 1 + $attendance['extra_in_person'] + $attendance['extra_alien']; + $cancel_array[$attendance['user_id']] = $attendance; + }else if($attendance['can_invite']==1) { + $cnt_can_invite++; + $att_array[$attendance['attend_time']][$attendance_pokemon][] = $attendance; + } + } + } + + // Get attendances with invite beggars + // Using a separate query so they can be displayed in the order they're in the db (who voted first) + $rs_attendance_want_inv = my_query(' + SELECT attendance.*, + users.name, + users.nick, + users.trainername, + users.display_name, + users.trainercode, + users.level, + users.team + FROM attendance + LEFT JOIN users + ON attendance.user_id = users.user_id + WHERE raid_id = ? + AND want_invite = 1 + AND cancel = 0 + AND raid_done = 0 + ' . $hide_users_sql . ' + AND attend_time IS NOT NULL + ORDER BY attend_time, + ' . $order_by_sql . ' + attendance.id + ', [$raid['id']] + ); + while ($attendance = $rs_attendance_want_inv->fetch()) { + // Attendance found + $cnt_want_invite = 1; + $attendance = check_trainername($attendance); + + // Fill attendance array with results + if($combine_attendances) { + $att_array[$attendance['attend_time']][0][] = $attendance; + }else { + $att_array[$attendance['attend_time']][$attendance['pokemon']][] = $attendance; + } + } + // Raid has started and has participants + if($time_now > $raid['start_time']) { + // Add raid is done message. + if($raid['raid_ended']) { + $msg = raid_poll_message($msg, '' . getPublicTranslation('raid_done') . '' . CR); + // Add time left message. } else { - // Gym note? - if(!empty($raid['gym_note'])) { - $msg = raid_poll_message($msg, EMOJI_INFO . SP . $raid['gym_note'] . CR); - } + $msg = raid_poll_message($msg, getPublicTranslation('raid') . ' — ' . getPublicTranslation('still') . ' ' . $time_left . 'h' . CR); + } + if($cnt_all > 0 || $buttons_hidden) { + // Display raid boss CP values. + $pokemon_cp = get_formatted_pokemon_cp($raid_pokemon_info, true); + $msg = raid_poll_message($msg, (!empty($pokemon_cp)) ? ($pokemon_cp . CR) : '', true); + } + } + + // Hide info if buttons are hidden + if($buttons_hidden) { + // Show message that voting is not possible! + $msg = raid_poll_message($msg, CR . '' . getPublicTranslation('raid_info_no_voting') . ' ' . CR); + } else { + // Gym note? + if(!empty($raid['gym_note'])) { + $msg = raid_poll_message($msg, EMOJI_INFO . SP . $raid['gym_note'] . CR); + } - // Add Ex-Raid Message if Pokemon is in Ex-Raid-List. - if($raid['event'] == EVENT_ID_EX) { - $msg = raid_poll_message($msg, CR . EMOJI_WARN . ' ' . getPublicTranslation('exraid_pass') . ' ' . EMOJI_WARN . CR); - } + // Add Ex-Raid Message if Pokemon is in Ex-Raid-List. + if($raid['event'] == EVENT_ID_EX) { + $msg = raid_poll_message($msg, CR . EMOJI_WARN . ' ' . getPublicTranslation('exraid_pass') . ' ' . EMOJI_WARN . CR); + } + // Add hint that remote participation is not possible + if($raid_local_only) { + $msg = raid_poll_message($msg, CR . EMOJI_WARN . SP . getPublicTranslation('no_remote_parcipants') . CR); + } - // Add event description - if($raid['event_description'] != NULL && $raid['event_description'] != "") { - $msg = raid_poll_message($msg, CR . "".$raid['event_name']."" . CR); - $msg = raid_poll_message($msg, $raid['event_description'] . CR); - } + // Add event description + if($raid['event_description'] != NULL && $raid['event_description'] != "") { + $msg = raid_poll_message($msg, CR . "".$raid['event_name']."" . CR); + $msg = raid_poll_message($msg, $raid['event_description'] . CR); + } - // Add event note - if($raid['event_note'] != NULL && $raid['event_note'] != "") { - $msg = raid_poll_message($msg, CR . $raid['event_note'] . CR); + // Add event note + if($raid['event_note'] != NULL && $raid['event_note'] != "") { + $msg = raid_poll_message($msg, CR . $raid['event_note'] . CR); + } + // Add attendances message. + if ($cnt_all > 0) { + // Init previous attend time and pokemon + $previous_att_time = 'FIRST_RUN'; + $previous_pokemon = 'FIRST_RUN'; + + // Add hint for remote attendances. + if(!$raid_local_only && ($cnt_remote > 0 || $cnt_want_invite > 0 || $cnt_extra_alien > 0)) { + $remote_max_msg = str_replace('REMOTE_MAX_USERS', $config->RAID_REMOTEPASS_USERS_LIMIT, getPublicTranslation('remote_participants_max')); + $msg = raid_poll_message($msg, CR . ($cnt_remote > 0 ? EMOJI_REMOTE : '') . ($cnt_extra_alien > 0 ? EMOJI_ALIEN : '') . ($cnt_want_invite > 0 ? EMOJI_WANT_INVITE : '') . SP . getPublicTranslation('remote_participants') . SP . '' . $remote_max_msg . '' . CR); + } + // Add hint for attendees that only invite. + if(!$raid_local_only && $cnt_can_invite > 0) { + $msg = raid_poll_message($msg, CR . EMOJI_CAN_INVITE . SP . getPublicTranslation('alert_can_invite') . CR); + } + // Add start raid message + if($cnt_all > 0 && $config->RAID_POLL_SHOW_START_LINK) { + $msg = raid_poll_message($msg, CR . '' . str_replace('START_CODE', '' . getPublicTranslation('telegram_bot_start') . '', getPublicTranslation('start_raid')) . '' . SP . '' . getPublicTranslation('start_raid_info') . '' . CR); + } + // Add hint for late attendances. + if($config->RAID_LATE_MSG && $cnt_latewait > 0) { + $late_wait_msg = ""; + if($config->RAID_LATE_TIME > 0) { + $late_wait_msg = str_replace('RAID_LATE_TIME', $config->RAID_LATE_TIME, getPublicTranslation('late_participants_wait')); } - // Add attendances message. - if ($cnt_all > 0) { - // Init previous attend time and pokemon - $previous_att_time = 'FIRST_RUN'; - $previous_pokemon = 'FIRST_RUN'; - - // Add hint for remote attendances. - if($cnt_remote > 0 || $cnt_want_invite > 0 || $cnt_extra_alien > 0) { - $remote_max_msg = str_replace('REMOTE_MAX_USERS', $config->RAID_REMOTEPASS_USERS_LIMIT, getPublicTranslation('remote_participants_max')); - $msg = raid_poll_message($msg, CR . ($cnt_remote > 0 ? EMOJI_REMOTE : '') . ($cnt_extra_alien > 0 ? EMOJI_ALIEN : '') . ($cnt_want_invite > 0 ? EMOJI_WANT_INVITE : '') . SP . getPublicTranslation('remote_participants') . SP . '' . $remote_max_msg . '' . CR); + $msg = raid_poll_message($msg, CR . EMOJI_LATE . '' . getPublicTranslation('late_participants') . ' ' . $late_wait_msg . '' . CR); + } + // For each attendance. + foreach($att_array as $att_time => $att_time_row) { + // Set current attend time and pokemon + $current_att_time = $att_time; + $dt_att_time = dt2time($current_att_time); + foreach($att_time_row as $att_pokemon => $att_pokemon_row) { + $current_pokemon = $att_pokemon; + $string_trainernames = ''; + foreach($att_pokemon_row as $att_row) { + // Add section/header for time + if($previous_att_time != $current_att_time) { + // Add to message. + if($raid['event_vote_key_mode'] == 1) { + // When vote key mode is set to 1, only display "Participating" in title, since no other timeslot selections are available + $msg = raid_poll_message($msg, CR . '' . getPublicTranslation('participating'). ''); + }else { + $msg = raid_poll_message($msg, CR . '' . (($current_att_time == ANYTIME) ? (getPublicTranslation('anytime')) : ($dt_att_time)) . ''); + } + + // Hide counts if other pokemon got selected. Show them in pokemon headers instead of attend time header + if ($cnt_array[$current_att_time][$current_pokemon]['total'] > 0 && $cnt_array[$current_att_time]['other_pokemon'] == 0) { + $msg = raid_poll_print_counts($msg, $cnt_array[$current_att_time][$current_pokemon]); + }else { + $msg = raid_poll_message($msg, CR ); + } } - // Add hint for attendees that only invite. - if($cnt_can_invite > 0) { - $msg = raid_poll_message($msg, CR . EMOJI_CAN_INVITE . SP . getPublicTranslation('alert_can_invite') . CR); - } - // Add start raid message - if($cnt_all > 0 && $config->RAID_POLL_SHOW_START_LINK) { - $msg = raid_poll_message($msg, CR . '' . str_replace('START_CODE', '' . getPublicTranslation('telegram_bot_start') . '', getPublicTranslation('start_raid')) . '' . SP . '' . getPublicTranslation('start_raid_info') . '' . CR); + + // Add section/header for pokemon + if($previous_pokemon != $current_pokemon || $previous_att_time != $current_att_time) { + // Only display the pokemon titles if other pokemon than raid pokemon and any pokemon was selected + if($cnt_array[$current_att_time]['other_pokemon'] > 0 ) { + // Add pokemon name. + $pokemon_id_form = explode("-",$current_pokemon,2); + $msg = raid_poll_message($msg, ($current_pokemon == 0) ? ('' . getPublicTranslation('any_pokemon') . '') : ('' . get_local_pokemon_name($pokemon_id_form[0],$pokemon_id_form[1], $config->LANGUAGE_PUBLIC) . '')); + + // Add counts to message. + $msg = raid_poll_print_counts($msg, $cnt_array[$current_att_time][$current_pokemon]); + } } - // Add hint for late attendances. - if($config->RAID_LATE_MSG && $cnt_latewait > 0) { - $late_wait_msg = ""; - if($config->RAID_LATE_TIME > 0) { - $late_wait_msg = str_replace('RAID_LATE_TIME', $config->RAID_LATE_TIME, getPublicTranslation('late_participants_wait')); - } - $msg = raid_poll_message($msg, CR . EMOJI_LATE . '' . getPublicTranslation('late_participants') . ' ' . $late_wait_msg . '' . CR); + + if($config->RAID_POLL_ENABLE_HYPERLINKS_IN_NAMES) { + $trainername = '' . htmlspecialchars($att_row['name']) . ' '; + }else { + $trainername = htmlspecialchars($att_row['name']) . ' '; } - // For each attendance. - foreach($att_array as $att_time => $att_time_row) { - // Set current attend time and pokemon - $current_att_time = $att_time; - $dt_att_time = dt2time($current_att_time); - foreach($att_time_row as $att_pokemon => $att_pokemon_row) { - $current_pokemon = $att_pokemon; - $string_trainernames = ''; - foreach($att_pokemon_row as $att_row) { - // Add section/header for time - if($previous_att_time != $current_att_time) { - // Add to message. - if($raid['event_vote_key_mode'] == 1) { - // When vote key mode is set to 1, only display "Participating" in title, since no other timeslot selections are available - $msg = raid_poll_message($msg, CR . '' . getPublicTranslation('participating'). ''); - }else { - $msg = raid_poll_message($msg, CR . '' . (($current_att_time == ANYTIME) ? (getPublicTranslation('anytime')) : ($dt_att_time)) . ''); - } - - // Hide counts if other pokemon got selected. Show them in pokemon headers instead of attend time header - if ($cnt_array[$current_att_time][$current_pokemon]['total'] > 0 && $cnt_array[$current_att_time]['other_pokemon'] == 0) { - $msg = raid_poll_print_counts($msg, $cnt_array[$current_att_time][$current_pokemon]); - }else { - $msg = raid_poll_message($msg, CR ); - } - } - - // Add section/header for pokemon - if($previous_pokemon != $current_pokemon || $previous_att_time != $current_att_time) { - // Only display the pokemon titles if other pokemon than raid pokemon and any pokemon was selected - if($cnt_array[$current_att_time]['other_pokemon'] > 0 ) { - // Add pokemon name. - $pokemon_id_form = explode("-",$current_pokemon,2); - $msg = raid_poll_message($msg, ($current_pokemon == 0) ? ('' . getPublicTranslation('any_pokemon') . '') : ('' . get_local_pokemon_name($pokemon_id_form[0],$pokemon_id_form[1], true) . '')); - - // Add counts to message. - $msg = raid_poll_print_counts($msg, $cnt_array[$current_att_time][$current_pokemon]); - } - } - - if($config->RAID_POLL_ENABLE_HYPERLINKS_IN_NAMES) { - $trainername = '' . htmlspecialchars($att_row['name']) . ' '; - }else { - $trainername = htmlspecialchars($att_row['name']) . ' '; - } - if(isset($att_row['trainername']) && $config->RAID_POLL_SHOW_TRAINERNAME_STRING && $att_row['want_invite']) { - if(!empty($string_trainernames)) $string_trainernames .= ','; - $string_trainernames .= $att_row['trainername']; - } - // Add users: ARRIVED --- TEAM -- LEVEL -- NAME -- INVITE -- EXTRAPEOPLE - $msg = raid_poll_message($msg, ($att_row['arrived']) ? (EMOJI_HERE . ' ') : (($att_row['late']) ? (EMOJI_LATE . ' ') : '└ ')); - $msg = raid_poll_message($msg, ($att_row['team'] === NULL) ? ($GLOBALS['teams']['unknown'] . ' ') : ($GLOBALS['teams'][$att_row['team']] . ' ')); - $msg = raid_poll_message($msg, ($att_row['level'] == 0) ? ('00 ') : (($att_row['level'] < 10) ? ('0' . $att_row['level'] . ' ') : ('' . $att_row['level'] . ' '))); - $msg = raid_poll_message($msg, $trainername); - $msg = raid_poll_message($msg, ($att_row['remote']) ? (EMOJI_REMOTE) : ''); - $msg = raid_poll_message($msg, ($raid['event'] == EVENT_ID_EX && $att_row['invite']) ? (EMOJI_INVITE . ' ') : ''); - $msg = raid_poll_message($msg, ($att_row['extra_in_person']) ? ('+' . $att_row['extra_in_person'] . EMOJI_IN_PERSON . ' ') : ''); - $msg = raid_poll_message($msg, ($att_row['extra_alien']) ? ('+' . $att_row['extra_alien'] . EMOJI_ALIEN . ' ') : ''); - $msg = raid_poll_message($msg, ($att_row['want_invite']) ? (EMOJI_WANT_INVITE) : ''); - $msg = raid_poll_message($msg, ($att_row['can_invite']) ? (EMOJI_CAN_INVITE) : ''); - $msg = raid_poll_message($msg, ($config->RAID_POLL_SHOW_TRAINERCODE && ($att_row['want_invite'] || $att_row['can_invite']) && !is_null($att_row['trainercode'])) ? ' ' . $att_row['trainercode'] . ' ': ''); - - $msg = raid_poll_message($msg, CR); - - // Prepare next result - $previous_att_time = $current_att_time; - $previous_pokemon = $current_pokemon; - } - if(!empty($string_trainernames)) { - $msg = raid_poll_message($msg, '' . $string_trainernames . '' . CR); - } - } + if(isset($att_row['trainername']) && $config->RAID_POLL_SHOW_TRAINERNAME_STRING && $att_row['want_invite']) { + if(!empty($string_trainernames)) $string_trainernames .= ','; + $string_trainernames .= $att_row['trainername']; } + // Add users: ARRIVED --- TEAM -- LEVEL -- NAME -- INVITE -- EXTRAPEOPLE + $msg = raid_poll_message($msg, ($att_row['arrived']) ? (EMOJI_HERE . ' ') : (($att_row['late']) ? (EMOJI_LATE . ' ') : '└ ')); + $msg = raid_poll_message($msg, ($att_row['team'] === NULL) ? ($GLOBALS['teams']['unknown'] . ' ') : ($GLOBALS['teams'][$att_row['team']] . ' ')); + $msg = raid_poll_message($msg, ($att_row['level'] == 0) ? ('00 ') : (($att_row['level'] < 10) ? ('0' . $att_row['level'] . ' ') : ('' . $att_row['level'] . ' '))); + $msg = raid_poll_message($msg, $trainername); + $msg = raid_poll_message($msg, ($att_row['remote']) ? (EMOJI_REMOTE) : ''); + $msg = raid_poll_message($msg, ($raid['event'] == EVENT_ID_EX && $att_row['invite']) ? (EMOJI_INVITE . ' ') : ''); + $msg = raid_poll_message($msg, ($att_row['extra_in_person']) ? ('+' . $att_row['extra_in_person'] . EMOJI_IN_PERSON . ' ') : ''); + $msg = raid_poll_message($msg, ($att_row['extra_alien']) ? ('+' . $att_row['extra_alien'] . EMOJI_ALIEN . ' ') : ''); + $msg = raid_poll_message($msg, ($att_row['want_invite']) ? (EMOJI_WANT_INVITE) : ''); + $msg = raid_poll_message($msg, ($att_row['can_invite']) ? (EMOJI_CAN_INVITE) : ''); + $msg = raid_poll_message($msg, ($config->RAID_POLL_SHOW_TRAINERCODE && ($att_row['want_invite'] || $att_row['can_invite']) && !is_null($att_row['trainercode'])) ? ' ' . $att_row['trainercode'] . ' ': ''); + + $msg = raid_poll_message($msg, CR); + + // Prepare next result + $previous_att_time = $current_att_time; + $previous_pokemon = $current_pokemon; + } + if(!empty($string_trainernames)) { + $msg = raid_poll_message($msg, '' . $string_trainernames . '' . CR); + } } + } + } - // Canceled or done? - if(!$config->RAID_POLL_HIDE_DONE_CANCELED && ($cnt_cancel > 0 || $cnt_done > 0)) { - // Init cancel_done value. - $cancel_done = 'CANCEL'; - $array_cancel_done = array_merge($cancel_array,$done_array); - // For each canceled / done. - foreach ($array_cancel_done as $row) { - // Attend time. - $dt_att_time = dt2time($row['attend_time']); - - // Add section/header for canceled - if($row['cancel'] == 1 && $cancel_done == 'CANCEL') { - $msg = raid_poll_message($msg, CR . TEAM_CANCEL . ' ' . getPublicTranslation('cancelled') . ': ' . '[' . $cnt_cancel . ']' . CR); - $cancel_done = 'DONE'; - } - - // Add section/header for canceled - if($row['raid_done'] == 1 && $cancel_done == 'CANCEL' || $row['raid_done'] == 1 && $cancel_done == 'DONE') { - $msg = raid_poll_message($msg, CR . TEAM_DONE . ' ' . getPublicTranslation('finished') . ': ' . '[' . $cnt_done . ']' . CR); - $cancel_done = 'END'; - } - if($config->RAID_POLL_ENABLE_HYPERLINKS_IN_NAMES) { - $trainername = '' . htmlspecialchars($row['name']) . ' '; - }else { - $trainername = htmlspecialchars($row['name']) . ' '; - } - - // Add users: TEAM -- LEVEL -- NAME -- CANCELED/DONE -- EXTRAPEOPLE - $msg = raid_poll_message($msg, ($row['team'] === NULL) ? ('└ ' . $GLOBALS['teams']['unknown'] . ' ') : ('└ ' . $GLOBALS['teams'][$row['team']] . ' ')); - $msg = raid_poll_message($msg, ($row['level'] == 0) ? ('00 ') : (($row['level'] < 10) ? ('0' . $row['level'] . ' ') : ('' . $row['level'] . ' '))); - $msg = raid_poll_message($msg, $trainername); - $msg = raid_poll_message($msg, ($raid['event'] == EVENT_ID_EX && $row['invite']) ? (EMOJI_INVITE . ' ') : ''); - if($raid['event_vote_key_mode'] != 1) { - $msg = raid_poll_message($msg, ($row['cancel'] == 1 || $row['raid_done'] == 1) ? ('[' . (($row['attend_time'] == ANYTIME) ? (getPublicTranslation('anytime')) : ($dt_att_time)) . '] ') : ''); - } - $msg = raid_poll_message($msg, ($row['extra_in_person']) ? ('+' . $row['extra_in_person'] . EMOJI_IN_PERSON . ' ') : ''); - $msg = raid_poll_message($msg, ($row['extra_alien']) ? ('+' . $row['extra_alien'] . EMOJI_ALIEN . ' ') : ''); - $msg = raid_poll_message($msg, CR); - } + // Canceled or done? + if(!$config->RAID_POLL_HIDE_DONE_CANCELED && ($cnt_cancel > 0 || $cnt_done > 0)) { + // Init cancel_done value. + $cancel_done = 'CANCEL'; + $array_cancel_done = array_merge($cancel_array,$done_array); + // For each canceled / done. + foreach ($array_cancel_done as $row) { + // Attend time. + $dt_att_time = dt2time($row['attend_time']); + + // Add section/header for canceled + if($row['cancel'] == 1 && $cancel_done == 'CANCEL') { + $msg = raid_poll_message($msg, CR . TEAM_CANCEL . ' ' . getPublicTranslation('cancelled') . ': ' . '[' . $cnt_cancel . ']' . CR); + $cancel_done = 'DONE'; } - // Add no attendance found message. - if ($cnt_all + $cnt_cancel + $cnt_done == 0) { - $msg = raid_poll_message($msg, CR . getPublicTranslation('no_participants_yet') . CR); + // Add section/header for canceled + if($row['raid_done'] == 1 && $cancel_done == 'CANCEL' || $row['raid_done'] == 1 && $cancel_done == 'DONE') { + $msg = raid_poll_message($msg, CR . TEAM_DONE . ' ' . getPublicTranslation('finished') . ': ' . '[' . $cnt_done . ']' . CR); + $cancel_done = 'END'; + } + if($config->RAID_POLL_ENABLE_HYPERLINKS_IN_NAMES) { + $trainername = '' . htmlspecialchars($row['name']) . ' '; + }else { + $trainername = htmlspecialchars($row['name']) . ' '; } - } - - //Add custom message from the config. - if (!empty($config->MAP_URL)) { - $msg = raid_poll_message($msg, CR . $config->MAP_URL); - } - // Display creator. - if($config->RAID_POLL_SHOW_CREATOR) { - $msg = raid_poll_message($msg, ($raid['user_id'] && $raid['name']) ? (CR . getPublicTranslation('created_by') . ': ' . htmlspecialchars($raid['name']) . '') : ''); + // Add users: TEAM -- LEVEL -- NAME -- CANCELED/DONE -- EXTRAPEOPLE + $msg = raid_poll_message($msg, ($row['team'] === NULL) ? ('└ ' . $GLOBALS['teams']['unknown'] . ' ') : ('└ ' . $GLOBALS['teams'][$row['team']] . ' ')); + $msg = raid_poll_message($msg, ($row['level'] == 0) ? ('00 ') : (($row['level'] < 10) ? ('0' . $row['level'] . ' ') : ('' . $row['level'] . ' '))); + $msg = raid_poll_message($msg, $trainername); + $msg = raid_poll_message($msg, ($raid['event'] == EVENT_ID_EX && $row['invite']) ? (EMOJI_INVITE . ' ') : ''); + if($raid['event_vote_key_mode'] != 1) { + $msg = raid_poll_message($msg, ($row['cancel'] == 1 || $row['raid_done'] == 1) ? ('[' . (($row['attend_time'] == ANYTIME) ? (getPublicTranslation('anytime')) : ($dt_att_time)) . '] ') : ''); + } + $msg = raid_poll_message($msg, ($row['extra_in_person']) ? ('+' . $row['extra_in_person'] . EMOJI_IN_PERSON . ' ') : ''); + $msg = raid_poll_message($msg, ($row['extra_alien']) ? ('+' . $row['extra_alien'] . EMOJI_ALIEN . ' ') : ''); + $msg = raid_poll_message($msg, CR); + } } - // Add update time and raid id to message. - $msg = raid_poll_message($msg, CR . '' . getPublicTranslation('updated') . ': ' . dt2time('now', 'H:i:s') . ''); - if($inline && ($buttons_hidden or ($raid['end_time'] < $time_now && $config->RAID_ENDED_HIDE_KEYS))) { - // Only case this is needed anymore is a poll shared via inline that has no vote keys - $msg = raid_poll_message($msg, SP . SP . substr(strtoupper($config->BOT_ID), 0, 1) . '-ID = ' . $raid['id']); + // Add no attendance found message. + if ($cnt_all + $cnt_cancel + $cnt_done == 0) { + $msg = raid_poll_message($msg, CR . getPublicTranslation('no_participants_yet') . CR); } - // Return the message. - return $msg; + } + + //Add custom message from the config. + if (!empty($config->MAP_URL)) { + $msg = raid_poll_message($msg, CR . $config->MAP_URL); + } + + // Display creator. + if($config->RAID_POLL_SHOW_CREATOR) { + $msg = raid_poll_message($msg, ($raid['user_id'] && $raid['name']) ? (CR . getPublicTranslation('created_by') . ': ' . htmlspecialchars($raid['name']) . '') : ''); + } + + // Add update time and raid id to message. + $msg = raid_poll_message($msg, CR . '' . getPublicTranslation('updated') . ': ' . dt2time('now', 'H:i:s') . ''); + if($inline && ($buttons_hidden or ($raid['raid_ended'] && $config->RAID_ENDED_HIDE_KEYS))) { + // Only case this is needed anymore is a poll shared via inline that has no vote keys + $msg = raid_poll_message($msg, SP . SP . substr(strtoupper($config->BOT_ID), 0, 1) . '-ID = ' . $raid['id']); + } + // Return the message. + return $msg; } /** * Print counts to raid poll headers. - * @param $msg - The array containing short and long poll messages - * @param $cnt_array - A row of cnt_array created in show_raid_poll for specific time/pokemon + * @param array $msg The array containing short and long poll messages + * @param array $cnt_array A row of cnt_array created in show_raid_poll for specific time/pokemon * @return array */ function raid_poll_print_counts($msg, $cnt_array) { - // Attendance counts - $count_in_person = $cnt_array['in_person']; - $count_remote = $cnt_array['remote']; - $count_extra_alien = $cnt_array['extra_alien']; - $count_want_invite = $cnt_array['want_invite']; - $count_late = $cnt_array['late']; - $count_total = (is_numeric($cnt_array['total'])?$cnt_array['total']:0); - - // Add to message. - $msg = raid_poll_message($msg, ' [' . ($count_total) . '] — '); - $msg = raid_poll_message($msg, (($count_in_person > 0) ? EMOJI_IN_PERSON . $count_in_person . ' ' : '')); - $msg = raid_poll_message($msg, (($count_remote > 0) ? EMOJI_REMOTE . $count_remote . ' ' : '')); - $msg = raid_poll_message($msg, (($count_extra_alien > 0) ? EMOJI_ALIEN . $count_extra_alien . ' ' : '')); - $msg = raid_poll_message($msg, (($count_want_invite > 0) ? EMOJI_WANT_INVITE . $count_want_invite . ' ' : '')); - $msg = raid_poll_message($msg, (($count_late > 0) ? EMOJI_LATE . $count_late . ' ' : '')); - $msg = raid_poll_message($msg, CR); - return $msg; + // Attendance counts + $count_in_person = $cnt_array['in_person']; + $count_remote = $cnt_array['remote']; + $count_extra_alien = $cnt_array['extra_alien']; + $count_want_invite = $cnt_array['want_invite']; + $count_late = $cnt_array['late']; + $count_total = (is_numeric($cnt_array['total'])?$cnt_array['total']:0); + + // Add to message. + $msg = raid_poll_message($msg, ' [' . ($count_total) . '] — '); + $msg = raid_poll_message($msg, (($count_in_person > 0) ? EMOJI_IN_PERSON . $count_in_person . ' ' : '')); + $msg = raid_poll_message($msg, (($count_remote > 0) ? EMOJI_REMOTE . $count_remote . ' ' : '')); + $msg = raid_poll_message($msg, (($count_extra_alien > 0) ? EMOJI_ALIEN . $count_extra_alien . ' ' : '')); + $msg = raid_poll_message($msg, (($count_want_invite > 0) ? EMOJI_WANT_INVITE . $count_want_invite . ' ' : '')); + $msg = raid_poll_message($msg, (($count_late > 0) ? EMOJI_LATE . $count_late . ' ' : '')); + $msg = raid_poll_message($msg, CR); + return $msg; } -?> diff --git a/logic/show_raid_poll_small.php b/logic/show_raid_poll_small.php index df738477..6f328709 100644 --- a/logic/show_raid_poll_small.php +++ b/logic/show_raid_poll_small.php @@ -1,74 +1,75 @@ ' . $raid['gym_name'] . '' . CR; - } + // Gym Name + if(!empty($raid['gym_name'])) { + $msg .= '' . $raid['gym_name'] . '' . CR; + } - // Address found. - if (!empty($raid['address'])) { - $msg .= '' . $raid['address'] . '' . CR2; - } + // Address found. + if (!empty($raid['address'])) { + $msg .= '' . $raid['address'] . '' . CR2; + } - // Pokemon - if(!empty($raid['pokemon'])) { - $msg .= '' . get_local_pokemon_name($raid['pokemon'], $raid['pokemon_form']) . ' ' . CR; - } - // Start time and end time - if(!empty($raid['start_time']) && !empty($raid['end_time'])) { - // Get raid times message. - $msg .= get_raid_times($raid, $override_language); - } + if(isset($raid['event_name']) && !empty($raid['event_name'])) { + $msg .= $raid['event_name'] . CR; + } + // Pokemon + if(!empty($raid['pokemon'])) { + $msg .= '' . get_local_pokemon_name($raid['pokemon'], $raid['pokemon_form']) . (isset($raid['shadow']) && $raid['shadow'] && !in_array($raid['pokemon'], EGGS) ? ' ' . getPublicTranslation('pokemon_form_shadow') : '') . ' ' . CR; + } + // Start time and end time + if(!empty($raid['start_time']) && !empty($raid['end_time'])) { + // Get raid times message. + $msg .= get_raid_times($raid, $botUser->userLanguage); + } - // Count attendances - $rs = my_query( - " - SELECT count(attend_time) AS count, - sum(remote = 0) AS count_in_person, - sum(remote = 1) + sum(case when remote = 1 then extra_in_person else 0 end) AS count_remote, - sum(case when remote = 0 then extra_in_person else 0 end) AS extra_in_person, - sum(extra_alien) AS extra_alien - FROM attendance - LEFT JOIN users - ON attendance.user_id = users.user_id - WHERE raid_id = {$raid['id']} - AND attend_time IS NOT NULL - AND raid_done != 1 - AND cancel != 1 - " - ); + // Count attendances + $rs = my_query(' + SELECT count(attend_time) AS count, + sum(remote = 0) AS count_in_person, + sum(remote = 1) + sum(case when remote = 1 then extra_in_person else 0 end) AS count_remote, + sum(case when remote = 0 then extra_in_person else 0 end) AS extra_in_person, + sum(extra_alien) AS extra_alien + FROM attendance + LEFT JOIN users + ON attendance.user_id = users.user_id + WHERE raid_id = ? + AND attend_time IS NOT NULL + AND raid_done != 1 + AND cancel != 1 + ', [$raid['id']] + ); - $row = $rs->fetch(); + $row = $rs->fetch(); - // Add to message. - if ($row['count'] > 0) { - // Count by team. - $count_in_person = $row['count_in_person']; - $count_remote = $row['count_remote']; - $extra_in_person = $row['extra_in_person']; - $extra_alien = $row['extra_alien']; + // Add to message. + if ($row['count'] < 1) { + $msg .= getTranslation('no_participants') . CR; + return $msg; + } + // Count by team. + $count_in_person = $row['count_in_person']; + $count_remote = $row['count_remote']; + $extra_in_person = $row['extra_in_person']; + $extra_alien = $row['extra_alien']; - // Add to message. - $msg .= EMOJI_GROUP . ' ' . ($count_in_person + $count_remote + $extra_in_person + $extra_alien) . ' — '; - $msg .= (($count_in_person > 0) ? EMOJI_IN_PERSON . ($count_in_person + $extra_in_person) . ' ' : ''); - $msg .= (($count_remote > 0) ? EMOJI_REMOTE . $count_remote . ' ' : ''); - $msg .= (($extra_alien > 0) ? EMOJI_ALIEN . $extra_alien . ' ' : ''); - $msg .= CR; - } else { - $msg .= getTranslation('no_participants') . CR; - } + // Add to message. + $msg .= EMOJI_GROUP . ' ' . ($count_in_person + $count_remote + $extra_in_person + $extra_alien) . ' — '; + $msg .= (($count_in_person > 0) ? EMOJI_IN_PERSON . ($count_in_person + $extra_in_person) . ' ' : ''); + $msg .= (($count_remote > 0) ? EMOJI_REMOTE . $count_remote . ' ' : ''); + $msg .= (($extra_alien > 0) ? EMOJI_ALIEN . $extra_alien . ' ' : ''); + $msg .= CR; - return $msg; + return $msg; } - -?> diff --git a/logic/show_trainerinfo.php b/logic/show_trainerinfo.php index 70765af1..bd3e298e 100644 --- a/logic/show_trainerinfo.php +++ b/logic/show_trainerinfo.php @@ -1,4 +1,5 @@ ' . getPublicTranslation('trainerinfo') . ':' . CR; - $msg .= getPublicTranslation('trainer_set_your_info') . CR . CR; - $msg .= getPublicTranslation('trainer_set_your_info_done') . CR . CR; + // Instructions + $msg = '' . getPublicTranslation('trainerinfo') . ':' . CR; + $msg .= getPublicTranslation('trainer_set_your_info') . CR . CR; + $msg .= getPublicTranslation('trainer_set_your_info_done') . CR . CR; - // Show user info? - if($show) { - $msg .= '' . getPublicTranslation('your_trainer_info') . '' . CR; - $msg .= get_user($update['callback_query']['from']['id']) . CR; - } + // Show user info? + if($show) { + $msg .= '' . getPublicTranslation('your_trainer_info') . '' . CR; + $msg .= get_user($update['callback_query']['from']['id']) . CR; + } - $msg .= '' . getPublicTranslation('updated') . ': ' . dt2time('now', 'H:i:s') . ''; + $msg .= '' . getPublicTranslation('updated') . ': ' . dt2time('now', 'H:i:s') . ''; - return $msg; + return $msg; } - -?> diff --git a/core/bot/logic/sql_utils.php b/logic/sql_utils.php similarity index 52% rename from core/bot/logic/sql_utils.php rename to logic/sql_utils.php index c95c1218..a3dc2417 100644 --- a/core/bot/logic/sql_utils.php +++ b/logic/sql_utils.php @@ -9,11 +9,10 @@ function run_sql_file($file) { if (!$dbh) { error_log('No DB handle!'); return false; - } + } try { $query = file_get_contents($file); - $statement = $dbh->prepare( $query ); - $statement->execute(); + $dbh->exec( $query ); } catch (PDOException $exception) { info_log('DB upgrade failed: ' . $exception->getMessage()); @@ -27,40 +26,41 @@ function run_sql_file($file) { /** * DB query wrapper that supports binds. - * @param $query - * @param binds + * @param $query string + * @param $binds Array * @return PDOStatement */ function my_query($query, $binds=null) { - global $dbh; - global $config; + global $dbh; + global $config; + require_once(ROOT_PATH . '/logic/debug.php'); - try { - $stmt = $dbh->prepare($query); - $stmt->execute($binds); - } catch (PDOException $exception) { - // The message will be output in the global handler, we just need to extract the failing query - error_log('The following query failed:'); - log_query($stmt, $binds, 'error_log'); - throw $exception; - } finally { - debug_log_sql('Query success', '$'); - if($config->DEBUG_SQL) { - log_query($stmt, $binds, 'debug_log_sql'); - } + try { + $stmt = $dbh->prepare($query); + $stmt->execute($binds); + } catch (PDOException $exception) { + // The message will be output in the global handler, we just need to extract the failing query + error_log('The following query failed:'); + log_query($stmt, print_r($binds, true), 'error_log'); + throw $exception; + } finally { + debug_log_sql('Query success', '$'); + if($config->DEBUG_SQL) { + log_query($stmt, print_r($binds, true), 'debug_log_sql'); } - return $stmt; + } + return $stmt; } // Debug log the full statement with parameters function log_query($stmt, $binds, $logger='debug_log_sql'){ - ob_start(); - $stmt->debugDumpParams(); - $debug = ob_get_contents(); - ob_end_clean(); - $logger($debug); - $logger('The parameters bound were:'); - if($binds === null) $binds = 'null'; - $logger($binds); + ob_start(); + $stmt->debugDumpParams(); + $debug = ob_get_contents(); + ob_end_clean(); + $logger($debug); + $logger('The parameters bound were:'); + if($binds === null) $binds = 'null'; + $logger($binds); } diff --git a/logic/update_raid_poll.php b/logic/update_raid_poll.php index b1420f64..4357d518 100644 --- a/logic/update_raid_poll.php +++ b/logic/update_raid_poll.php @@ -1,4 +1,6 @@ null, - 'message_id' => $update['callback_query']['inline_message_id'], - 'type' => 'poll_text', - ]; - // For updating a poll - }else if(isset($update['push'])) { - $chat_and_message[] = [ - 'chat_id' => $update['push']['chat_id'], - 'message_id' => $update['push']['message_id'], - 'type' => $update['push']['type'], - ]; - } - // If neither of the methods above yielded results, or update came from a inline poll, check cleanup table for chat messages to update - if(empty($chat_and_message) or isset($update['callback_query']['inline_message_id'])) { - $rs_chann = my_query(' - SELECT chat_id, message_id, type - FROM cleanup - WHERE raid_id = ' . $raid_id - ); - if ($rs_chann->rowCount() > 0) { - while($chat = $rs_chann->fetch()) { - $chat_and_message[] = ['chat_id' => $chat['chat_id'], 'message_id' => $chat['message_id'], 'type' => $chat['type']]; - } - }else { - if(is_array($tg_json)) return $tg_json; - else return []; + global $config; + $chat_and_message = []; + // Parsing the update type here, since the callback_query can come from regular message or inline message + if(isset($update['callback_query']['inline_message_id'])) { + $chat_and_message[] = [ + 'chat_id' => null, + 'message_id' => $update['callback_query']['inline_message_id'], + 'type' => 'poll_text', + ]; + // For updating a poll + }else if(isset($update['push'])) { + $chat_and_message[] = $update['push']; + } + // If neither of the methods above yielded results, or update came from a inline poll, check cleanup table for chat messages to update + if(empty($chat_and_message) or isset($update['callback_query']['inline_message_id'])) { + if($update_photo) $photo_query = 'AND (type = \'photo\' OR type = \'poll_photo\')'; else $photo_query = ''; + $rs_chann = my_query(' + SELECT chat_id, message_id, thread_id, type, media_unique_id + FROM cleanup + WHERE raid_id = ? + ' . $photo_query,[$raid_id] + ); + if ($rs_chann->rowCount() > 0 || false) { + while($chat = $rs_chann->fetch()) { + $chat_and_message[] = $chat; + } + }else { + if($update === false) { + if(is_array($tg_json)) return $tg_json; + else return []; + } + $chatId = $update[$update['type']]['message']['chat']['id']; + $messageId = $update[$update['type']]['message']['message_id']; + if(isset($update[$update['type']]['message']['text']) && !empty($update[$update['type']]['message']['text'])) { + $type = 'poll_text'; + }else if(isset($update[$update['type']]['message']['caption']) && !empty($update[$update['type']]['message']['caption'])) { + $type = 'poll_photo'; + }else if(isset($update[$update['type']]['message']['venue']) && !empty($update[$update['type']]['message']['venue'])) { + $type = 'poll_venue'; + }else if(isset($update[$update['type']]['message']['photo']) && !isset($update[$update['type']]['message']['caption'])) { + $type = 'photo'; + } + $unique_id = null; + if(isset($update[$update['type']]['message']['photo'])) { + $largest_size = 0; + foreach($update[$update['type']]['message']['photo'] as $photo) { + if($photo['file_size'] < $largest_size) continue; + $largest_size = $photo['file_size']; + $save_id = $photo['file_id']; + $unique_id = $photo['file_unique_id']; } + } + $chat_and_message[] = ['chat_id' => $chatId, 'message_id' => $messageId, 'type' => $type, 'media_unique_id' => $unique_id]; } - // Get the raid data by id. - if($raid == false) $raid = get_raid($raid_id); + } + // Get the raid data by id. + if($raid == false) $raid = get_raid($raid_id); - // Message - make sure to not exceed Telegrams 1024 characters limit for caption - $text = show_raid_poll($raid); - $post_text = false; - if(array_key_exists('short', $text)) { - $msg_short_len = strlen(utf8_decode($text['short'])); - debug_log($msg_short_len, 'Raid poll short message length:'); - // Message short enough? - if($msg_short_len >= 1024) { - // Use full text and reset text to true regardless of prior value - $post_text = true; - } - } else { - // Use full text and reset text to true regardless of prior value - $post_text = true; + // Message - make sure to not exceed Telegrams 1024 characters limit for caption + $text = show_raid_poll($raid); + $post_text = false; + if(array_key_exists('short', $text)) { + $msg_short_len = strlen(mb_convert_encoding($text['short'], 'ISO-8859-1')); + debug_log($msg_short_len, 'Raid poll short message length:'); + // Message short enough? + if($msg_short_len >= 1024) { + // Use full text and reset text to true regardless of prior value + $post_text = true; } - $keys = keys_vote($raid); + } else { + // Use full text and reset text to true regardless of prior value + $post_text = true; + } + $keys = keys_vote($raid); - // Telegram JSON array. - if($tg_json == false) $tg_json = []; - if($config->RAID_PICTURE) { - require_once(LOGIC_PATH . '/raid_picture.php'); - } + // Telegram JSON array. + if($tg_json == false) $tg_json = []; - foreach($chat_and_message as $chat_id_msg_id) { - $chat = $chat_id_msg_id['chat_id']; - $message = $chat_id_msg_id['message_id']; - $type = $chat_id_msg_id['type']; - if($type == 'poll_photo') { - $picture_url = raid_picture_url($raid); - // If the poll message gets too long, we'll replace it with regular text based poll - if($post_text == true) { - // Delete raid picture and caption. - $tg_json[] = delete_message($chat, $message, true); - my_query("DELETE FROM cleanup WHERE chat_id = '{$chat}' AND message_id = '{$message}'"); + foreach($chat_and_message as $chat_id_msg_id) { + $chat = $chat_id_msg_id['chat_id']; + $message = $chat_id_msg_id['message_id']; + $thread_id = $chat_id_msg_id['thread_id'] ?? null; + $type = $chat_id_msg_id['type']; + if ($type == 'poll_text') { + $raid_picture_hide_level = explode(",",$config->RAID_PICTURE_HIDE_LEVEL); + $raid_picture_hide_pokemon = explode(",",$config->RAID_PICTURE_HIDE_POKEMON); - // Resend raid poll as text message. - send_photo($chat, $picture_url, '', [], [], false, $raid_id); - send_message($chat, $text['full'], $keys, ['disable_web_page_preview' => 'true'], false, $raid_id); - } else { - // Edit the picture and caption - if(!$skip_picture_update) { - $tg_json[] = editMessageMedia($message, $text['short'], $picture_url, $keys, $chat, ['disable_web_page_preview' => 'true'], true); - }else { - // Edit the caption. - $tg_json[] = editMessageCaption($message, $text['short'], $keys, $chat, ['disable_web_page_preview' => 'true'], true); - } - } - }else if ($type == 'poll_text') { - $raid_picture_hide_level = explode(",",$config->RAID_PICTURE_HIDE_LEVEL); - $raid_picture_hide_pokemon = explode(",",$config->RAID_PICTURE_HIDE_POKEMON); + // If poll type was text, and RAID_PICUTRE_AUTO_EXTEND is set true in config, we most likely want to update the poll with the short message + // Exceptions are: inline poll (chat = null) and events with raid picture hidden + if($config->RAID_PICTURE && $config->RAID_PICTURE_AUTOEXTEND && $raid['event_hide_raid_picture'] != 1 && $chat != null && !in_array($raid['level'], $raid_picture_hide_level) && !in_array($raid['pokemon'], $raid_picture_hide_pokemon) && !in_array($raid['pokemon'].'-'.$raid['pokemon_form'], $raid_picture_hide_pokemon)) { + $tg_json[] = editMessageText($message, $text['short'], $keys, $chat, ['disable_web_page_preview' => 'true'], true); + }else { + $tg_json[] = editMessageText($message, $text['full'], $keys, $chat, ['disable_web_page_preview' => 'true'], true); + } + continue; + } + require_once(LOGIC_PATH . '/raid_picture.php'); + if($type == 'poll_photo') { + // If the poll message gets too long, we'll replace it with regular text based poll + if($post_text == true) { + // Delete raid picture and caption. + $tg_json[] = delete_message($chat, $message, true); + my_query('DELETE FROM cleanup WHERE chat_id = ? AND message_id = ?', [$chat, $message]); - // If poll type was text, and RAID_PICUTRE_AUTO_EXTEND is set true in config, we most likely want to update the poll with the short message - // Exceptions are: inline poll (chat = null) and events with raid picture hidden - if($config->RAID_PICTURE && $config->RAID_PICTURE_AUTOEXTEND && $raid['event_hide_raid_picture'] != 1 && $chat != null && !in_array($raid['level'], $raid_picture_hide_level) && !in_array($raid['pokemon'], $raid_picture_hide_pokemon) && !in_array($raid['pokemon'].'-'.$raid['pokemon_form'], $raid_picture_hide_pokemon)) { - $tg_json[] = editMessageText($message, $text['short'], $keys, $chat, ['disable_web_page_preview' => 'true'], true); - }else { - $tg_json[] = editMessageText($message, $text['full'], $keys, $chat, ['disable_web_page_preview' => 'true'], true); - } - }else if ($type == 'photo' && !$skip_picture_update) { - $picture_url = raid_picture_url($raid, 1); - $tg_json[] = editMessageMedia($message, '', $picture_url, '', $chat, [], true); - } + $media_content = get_raid_picture($raid, true); + $raid['standalone_photo'] = true; // Inject this into raid array so we can pass it all the way to photo cache + // Resend raid poll as text message. + send_photo(create_chat_object([$chat, $thread_id]), $media_content[1], $media_content[0], '', [], [], false, $raid); + send_message(create_chat_object([$chat, $thread_id]), $text['full'], $keys, ['disable_web_page_preview' => 'true'], false, $raid_id); + continue; + } + $media_content = get_raid_picture($raid); + // Edit the picture and caption + if(!isset($media_content[2]) or $media_content[2] != $chat_id_msg_id['media_unique_id']) { + $tg_json[] = editMessageMedia($message, $text['short'], $media_content[1], $media_content[0], $keys, $chat, ['disable_web_page_preview' => 'true'], true, $raid); + }else { + // Edit the caption. + $tg_json[] = editMessageCaption($message, $text['short'], $keys, $chat, ['disable_web_page_preview' => 'true'], true); + } + }else if ($type == 'photo' && $update_photo) { + $media_content = get_raid_picture($raid, 1); + $raid['standalone_photo'] = true; // Inject this into raid array so we can pass it all the way to photo cache + if(!isset($media_content[2]) or $media_content[2] != $chat_id_msg_id['media_unique_id']) { + $tg_json[] = editMessageMedia($message, '', $media_content[1], $media_content[0], false, $chat, false, true, $raid); + } } - return $tg_json; + } + return $tg_json; } -?> \ No newline at end of file diff --git a/logic/user_tutorial.php b/logic/user_tutorial.php deleted file mode 100644 index 8548bcd1..00000000 --- a/logic/user_tutorial.php +++ /dev/null @@ -1,25 +0,0 @@ -prepare( $query ); - $statement->execute([":user_id"=>$user_id]); - $res = $statement->fetch(); - if($statement->rowCount() > 0) $result = $res['tutorial']; - else $result = 0; - debug_log("Result: ".$result); - return $result; - } catch (PDOException $exception) { - error_log($exception->getMessage()); - $dbh = null; - exit; - } -} -?> \ No newline at end of file diff --git a/logic/weather_keys.php b/logic/weather_keys.php index 43272bb9..c52c1b45 100644 --- a/logic/weather_keys.php +++ b/logic/weather_keys.php @@ -1,72 +1,50 @@ $GLOBALS['weather'][$i], - 'callback_data' => $pokedex_id . ':' . $action . ':' . $new_weather - ); - } + // Get the type, level and cp + $weather_value = $data['w'] ?? ''; + + // Init empty keys array. + $keys = []; + + // Max amount of weathers a pokemon raid boss can have is 3 which means 999 + // Keys will be shown up to 99 and when user is adding one more weather we exceed 99, so we remove the keys then + // This means we do not exceed the max amout of 3 weathers a pokemon can have :) + // And no, 99 is not a typo if you read my comment above :P + if($weather_value <= 99) { + // Get last number from weather array + end($GLOBALS['weather']); + $last = key($GLOBALS['weather']); + $buttonData = $data; + // Add buttons for each weather. + for ($i = 1; $i <= $last; $i = $i + 1) { + // Continue if weather got already selected + if (preg_match('/'.$i.'/', $weather_value)) + continue; + + // Set new weather. + $buttonData['w'] = $weather_value . $i; + + // Set keys. + $keys[] = button($GLOBALS['weather'][$i], $buttonData); } - - // Get the inline key array. - $keys = inline_key_array($keys, 3); - - // Save and Reset key - $keys[] = array( - array( - 'text' => EMOJI_DISK, - 'callback_data' => $pokedex_id . ':' . $action . ':' . $save_arg - ), - array( - 'text' => getTranslation('reset'), - 'callback_data' => $pokedex_id . ':' . $action . ':' . $reset_arg - ) - ); - - return $keys; + } + // Get the inline key array. + $keys = inline_key_array($keys, 3); + + $saveData = $resetData = $data; + $saveData['a'] = 'save'; + unset($resetData['w']); + // Save and Reset key + $keys[] = array( + button(EMOJI_DISK, $saveData), + button(getTranslation('reset'), $resetData) + ); + + return $keys; } - -?> diff --git a/metrics/index.php b/metrics/index.php index e30c31dc..e5214954 100644 --- a/metrics/index.php +++ b/metrics/index.php @@ -7,9 +7,9 @@ use Prometheus\RenderTextFormat; require_once __DIR__ . '/../core/bot/paths.php'; -require_once(CORE_BOT_PATH . '/logic/debug.php'); -require_once(CORE_BOT_PATH . '/logic/bearer_token.php'); require_once(CORE_BOT_PATH . '/config.php'); +require_once(ROOT_PATH . '/logic/debug.php'); +require_once(ROOT_PATH . '/logic/bearer_token.php'); // Authentication is done based on a Bearer Token provided as a header $bearer_token = getBearerToken(); diff --git a/mods/bot_lang.php b/mods/bot_lang.php index 15d3ec64..e289a53d 100644 --- a/mods/bot_lang.php +++ b/mods/bot_lang.php @@ -7,55 +7,38 @@ //debug_log($data); // Access check. -bot_access_check($update, 'trainer'); - +$botUser->accessCheck('trainer'); $keys = []; -if($data['arg'] != '0') { - $query = " - UPDATE users - SET lang_manual = 1, - lang= :lang - WHERE user_id = :user_id - "; - $q = $dbh->prepare($query); - $q->execute([ - 'lang' => $data['arg'], - 'user_id' => $update['callback_query']['from']['id'] - ]); - $new_lang_internal = $languages[$data['arg']]; - $msg = getTranslation('new_lang_saved', true, $new_lang_internal); - $keys[] = [ - [ - 'text' => getTranslation('back', true, $new_lang_internal), - 'callback_data' => '0:trainer:0' - ], - [ - 'text' => getTranslation('done', true, $new_lang_internal), - 'callback_data' => '0:exit:1' - ] - ]; - $callback_msg = $msg; +if(isset($data['l'])) { + $query = my_query(' + UPDATE users + SET lang_manual = 1, + lang= :lang + WHERE user_id = :user_id + ',[ + 'lang' => $data['l'], + 'user_id' => $update['callback_query']['from']['id'], + ]); + $new_lang_internal = $languages[$data['l']]; + $msg = getTranslation('new_lang_saved', $new_lang_internal); + $keys[0][] = button(getTranslation('back', $new_lang_internal), 'trainer'); + $keys[0][] = button(getTranslation('done', $new_lang_internal), ['exit', 'd' => '1']); + $callback_msg = $msg; } else { - foreach($languages as $lang_tg => $lang_internal) { - $keys[][] = [ - 'text' => getTranslation('lang_name', true, $lang_internal), - 'callback_data' => '0:bot_lang:'.$lang_tg - ]; - } - $keys[] = [ - [ - 'text' => getTranslation('back'), - 'callback_data' => '0:trainer:0' - ], - [ - 'text' => getTranslation('done'), - 'callback_data' => '0:exit:1' - ] - ]; - $msg = getTranslation('change_lang').':'; - $callback_msg = getTranslation('change_lang'); + $displayedLanguages = []; + foreach($languages as $lang_tg => $lang_internal) { + if(in_array($lang_internal, $displayedLanguages)) continue; + $keys[][] = button(getTranslation('lang_name', $lang_internal), ['bot_lang', 'l' => $lang_tg]); + $displayedLanguages[] = $lang_internal; + } + $keys[] = [ + button(getTranslation('back'), 'trainer'), + button(getTranslation('done'), ['exit', 'd' => '1']) + ]; + $msg = getTranslation('change_lang').':'; + $callback_msg = getTranslation('change_lang'); } // Answer callback. @@ -63,9 +46,3 @@ // Edit message. edit_message($update, $msg, $keys, false); - -// Exit. -$dbh = null; -exit(); - -?> \ No newline at end of file diff --git a/mods/change_trainercode.php b/mods/change_trainercode.php index b8aa396b..f3a0141b 100644 --- a/mods/change_trainercode.php +++ b/mods/change_trainercode.php @@ -7,37 +7,25 @@ $trainercode = preg_replace('/\D/', '', $update['message']['text']); // Check that Code is 12 digits long -if(strlen($trainercode)==12){ - // Store new Trainercode to DB - my_query( - " - UPDATE users - SET trainercode = '{$trainercode}' - WHERE user_id = {$target_user_id} - " - ); - - // Remove back button from previous message to avoid confusion - edit_message_keyboard($modifiers['old_message_id'], [], $target_user_id); - - // Create the keys. - $keys = [ - [ - [ - 'text' => getTranslation('back'), - 'callback_data' => '0:trainer:0' - ], - [ - 'text' => getTranslation('done'), - 'callback_data' => '0:exit:1' - ] - ] - ]; - - // confirm Trainercode-Change - send_message($target_user_id, getTranslation('trainercode_success').' '.$trainercode.'', $keys); -}else{ - // Trainer Code got unallowed Chars -> Error-Message - send_message($target_user_id, getTranslation('trainercode_fail')); - exit(); +if(strlen($trainercode) != 12){ + // Trainer Code got unallowed Chars -> Error-Message + send_message(create_chat_object([$target_user_id]), getTranslation('trainercode_fail')); + exit(); } +// Store new Trainercode to DB +my_query(' + UPDATE users + SET trainercode = ? + WHERE user_id = ? + ', [$trainercode, $target_user_id] +); + +// Remove back button from previous message to avoid confusion +edit_message_keyboard($modifiers['old_message_id'], [], $target_user_id); + +// Create the keys. +$keys[0][0] = button(getTranslation('back'), 'trainer'); +$keys[0][1] = button(getTranslation('done'), ['exit', 'd' => '1']); + +// confirm Trainercode-Change +send_message(create_chat_object([$target_user_id]), getTranslation('trainercode_success').' '.$trainercode.'', $keys); diff --git a/mods/change_trainername.php b/mods/change_trainername.php index 7d0abb4a..02c35bfd 100644 --- a/mods/change_trainername.php +++ b/mods/change_trainername.php @@ -5,40 +5,27 @@ $returnValue = preg_match('/^[A-Za-z0-9]{0,15}$/', $update['message']['text']); // Only numbers and alphabetic character allowed -if($returnValue){ - $trainername = $update['message']['text']; - // Store new Gamer-Name to DB - my_query( - " - UPDATE users - SET trainername = '{$trainername}', - display_name = IF(trainername is null, 0, 1) - WHERE user_id = {$userid} - " - ); - - // Remove back button from previous message to avoid confusion - edit_message_keyboard($modifiers['old_message_id'], [], $userid); +if(!$returnValue){ + // Trainer Name got unallowed Chars -> Error-Message + send_message(create_chat_object([$userid]), getTranslation('trainername_fail')); + exit(); +} +$trainername = $update['message']['text']; +// Store new Gamer-Name to DB +my_query(' + UPDATE users + SET trainername = ?, + display_name = IF(trainername is null, 0, 1) + WHERE user_id = ? + ', [$trainername, $userid] +); - // Create the keys. - $keys = [ - [ - [ - 'text' => getTranslation('back'), - 'callback_data' => '0:trainer:0' - ], - [ - 'text' => getTranslation('done'), - 'callback_data' => '0:exit:1' - ] - ] - ]; +// Remove back button from previous message to avoid confusion +edit_message_keyboard($modifiers['old_message_id'], [], $userid); - // confirm Name-Change - send_message($userid, getTranslation('trainername_success').' '.$trainername.'', $keys); -}else{ - // Trainer Name got unallowed Chars -> Error-Message - send_message($userid, getTranslation('trainername_fail')); - exit(); -} +// Create the keys. +$keys[0][] = button(getTranslation('back'), 'trainer'); +$keys[0][] = button(getTranslation('done'), ['exit', 'd' => '1']); +// confirm Name-Change +send_message(create_chat_object([$userid]), getTranslation('trainername_success').' '.$trainername.'', $keys); diff --git a/mods/code.php b/mods/code.php index 407905e2..d984497d 100644 --- a/mods/code.php +++ b/mods/code.php @@ -1,6 +1,7 @@ accessCheck('list'); // Set the raid id. -$raid_id = $data['id']; +$raid_id = $data['r']; // Set the arg. -$arg = $data['arg']; +$arg = $data['a'] ?? ''; // Telegram JSON array. $tg_json = array(); @@ -23,7 +24,7 @@ $raid = get_raid($raid_id); $gym_name = $raid['gym_name']; if(empty($gym_name)) { - $gym_name = ''; + $gym_name = ''; } $msg = $gym_name . CR; $raid_day = dt2date($raid['start_time']); @@ -35,114 +36,102 @@ // Public or private raid group? if($arg == 'public-unconfirmed' || $arg == 'public-send') { - // Log - debug_log('Public raid group'); - if($arg == 'public-send') { - // Groupcode - $group_code = getTranslation('start_raid_public') . SP . getTranslation('no_group_code'); - - // Send code via alarm function - $tg_json = alarm($raid_id,$update['callback_query']['from']['id'],'group_code_public',$group_code, $tg_json); - - // Init empty keys array. - $keys = []; - - // Build callback message string. - $callback_response = getTranslation('code_send'); - - // Set the message. - $msg .= '' . getTranslation('code_send') . '' . CR . CR; - $msg .= getTranslation('group_code') . ':' . CR; - $msg .= $group_code; - } else { - // Init empty keys array. - $keys = []; - - // Back and abort. - $keys[] = [ - [ - 'text' => EMOJI_INVITE, - 'callback_data' => $raid_id . ':code:public-send' - ], - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ]; - - // Build callback message string. - $callback_response = 'OK'; - - // Set the message. - $msg .= '' . getTranslation('start_raid_now') . ':' . CR . CR; - } + // Log + debug_log('Public raid group'); + if($arg == 'public-send') { + // Groupcode + $group_code = getTranslation('start_raid_public') . SP . getTranslation('no_group_code'); + + // Send code via alarm function + $tg_json = alarm($raid_id,$update['callback_query']['from']['id'],'group_code_public',$group_code, $tg_json); + + // Init empty keys array. + $keys = []; + + // Build callback message string. + $callback_response = getTranslation('code_send'); + + // Set the message. + $msg .= '' . getTranslation('code_send') . '' . CR . CR; + $msg .= getTranslation('group_code') . ':' . CR; + $msg .= $group_code; + } else { + // Init empty keys array. + $keys = []; + + // Back and abort. + $keys[0][0] = button(EMOJI_INVITE, ['code' ,'r' => $raid_id, 'a' => 'public-send']); + $keys[0][1] = button(getTranslation('abort'), 'exit'); + + // Build callback message string. + $callback_response = 'OK'; + + // Set the message. + $msg .= '' . getTranslation('start_raid_now') . ':' . CR . CR; + } } else { - // Get the current code - $data = explode("-", $arg); - $c1 = $data[0]; - $c2 = $data[1]; - $c3 = $data[2]; - - // Get pokemon names. - $p1 = ($c1 > 0) ? get_local_pokemon_name($c1, 0) : ''; - $p2 = ($c2 > 0) ? get_local_pokemon_name($c2, 0) : ''; - $p3 = ($c3 > 0) ? get_local_pokemon_name($c3, 0) : ''; - - // Action to do: Ask to send or add code? - $action = $data[3]; - - // Log - debug_log('Code #1: ' . $c1 . SP . '(' . $p1 . ')'); - debug_log('Code #2: ' . $c2 . SP . '(' . $p2 . ')'); - debug_log('Code #3: ' . $c3 . SP . '(' . $p3 . ')'); - debug_log('Action: ' . $action); - - // Add digits to cp - if($action == 'add') { - // Init empty keys array. - $keys = []; - - // Get the keys. - $keys = group_code_keys($raid_id, 'code', $arg); - - // Back and abort. - $keys[] = [ - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ]; - - // Build callback message string. - $callback_response = 'OK'; - - // Set the message. - $msg .= '' . getTranslation('group_code') . ':' . CR; - $msg .= ($c1 > 0) ? ($p1 . CR) : ''; - $msg .= ($c2 > 0) ? ($p2 . CR) : ''; - $msg .= ($c3 > 0) ? ($p3 . CR . CR . '' . getTranslation('start_raid_now') . ':' . CR) : ''; - - // Send code to users - } else if($action == 'send') { - // Groupcode - $group_code = $p1 . '--' . SP; - $group_code .= $p2 . '--' . SP; - $group_code .= $p3 . CR; - - // Send code via alarm function - $tg_json = alarm($raid_id,$update['callback_query']['from']['id'],'group_code_private',$group_code, $tg_json); - - // Init empty keys array. - $keys = []; - - // Build callback message string. - $callback_response = getTranslation('code_send'); - - // Set the message. - $msg .= '' . getTranslation('code_send') . '' . CR . CR; - $msg .= getTranslation('group_code') . ':' . CR; - $msg .= $group_code; - } + // Get the current code + $data = explode("-", $arg); + $c1 = $data[0]; + $c2 = $data[1]; + $c3 = $data[2]; + + // Get pokemon names. + $p1 = ($c1 > 0) ? get_local_pokemon_name($c1, 0) : ''; + $p2 = ($c2 > 0) ? get_local_pokemon_name($c2, 0) : ''; + $p3 = ($c3 > 0) ? get_local_pokemon_name($c3, 0) : ''; + + // Action to do: Ask to send or add code? + $action = $data[3]; + + // Log + debug_log('Code #1: ' . $c1 . SP . '(' . $p1 . ')'); + debug_log('Code #2: ' . $c2 . SP . '(' . $p2 . ')'); + debug_log('Code #3: ' . $c3 . SP . '(' . $p3 . ')'); + debug_log('Action: ' . $action); + + // Add digits to cp + if($action == 'add') { + require_once(LOGIC_PATH . '/group_code_keys.php'); + // Init empty keys array. + $keys = []; + + // Get the keys. + $keys = group_code_keys($raid_id, 'code', $arg); + + // Back and abort. + $keys[][] = button(getTranslation('abort'), 'exit'); + + // Build callback message string. + $callback_response = 'OK'; + + // Set the message. + $msg .= '' . getTranslation('group_code') . ':' . CR; + $msg .= ($c1 > 0) ? ($p1 . CR) : ''; + $msg .= ($c2 > 0) ? ($p2 . CR) : ''; + $msg .= ($c3 > 0) ? ($p3 . CR . CR . '' . getTranslation('start_raid_now') . ':' . CR) : ''; + + // Send code to users + } else if($action == 'send') { + // Groupcode + $group_code = $p1 . '--' . SP; + $group_code .= $p2 . '--' . SP; + $group_code .= $p3 . CR; + + // Send code via alarm function + $tg_json = alarm($raid_id,$update['callback_query']['from']['id'],'group_code_private',$group_code, $tg_json); + + // Init empty keys array. + $keys = []; + + // Build callback message string. + $callback_response = getTranslation('code_send'); + + // Set the message. + $msg .= '' . getTranslation('code_send') . '' . CR . CR; + $msg .= getTranslation('group_code') . ':' . CR; + $msg .= $group_code; + } } // Answer callback. @@ -153,6 +142,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/code_start.php b/mods/code_start.php index 73b88923..f69849b1 100644 --- a/mods/code_start.php +++ b/mods/code_start.php @@ -8,7 +8,7 @@ // Allow anyone to use /code // Check access. -//bot_access_check($update, 'list'); +//$botUser->accessCheck('list'); // Get raid $raid = get_raid($code_raid_id); @@ -23,49 +23,32 @@ // Raid ended already. if ($end_time > $now) { - // Set text and keys. - $gym_name = $raid['gym_name']; - if(empty($gym_name)) { - $gym_name = ''; - } - - $text .= $gym_name . CR; - $raid_day = dt2date($raid['start_time']); - $now = utcnow(); - $today = dt2date($now); - $start = dt2time($raid['start_time']); - $end = dt2time($raid['end_time']); - $text .= get_local_pokemon_name($raid['pokemon'], $raid['pokemon_form']) . SP . '-' . SP . (($raid_day == $today) ? '' : ($raid_day . ', ')) . $start . SP . getTranslation('to') . SP . $end . CR . CR; - - // Add exit key. - $keys = [ - [ - [ - 'text' => getTranslation('start_raid_public'), - 'callback_data' => $raid['id'] . ':code:public-unconfirmed' - ], - [ - 'text' => getTranslation('start_raid_private'), - 'callback_data' => $raid['id'] . ':code:0-0-0-add' - ] - ], - [ - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ] - ]; - - // Build message. - $msg = '' . getTranslation('start_raid_now') . ':' . CR . CR; - $msg .= $text; + // Set text and keys. + $gym_name = $raid['gym_name']; + if(empty($gym_name)) { + $gym_name = ''; + } + + $text .= $gym_name . CR; + $raid_day = dt2date($raid['start_time']); + $now = utcnow(); + $today = dt2date($now); + $start = dt2time($raid['start_time']); + $end = dt2time($raid['end_time']); + $text .= get_local_pokemon_name($raid['pokemon'], $raid['pokemon_form']) . SP . '-' . SP . (($raid_day == $today) ? '' : ($raid_day . ', ')) . $start . SP . getTranslation('to') . SP . $end . CR . CR; + + // Add exit key. + $keys[0][] = button(getTranslation('start_raid_public'), ['code', 'r' => $raid['id'], 'a' => 'public-unconfirmed']); + $keys[0][] = button(getTranslation('start_raid_private'), ['code', 'r' => $raid['id'], 'a' => '0-0-0-add']); + $keys[1][] = button(getTranslation('abort'), 'exit'); + + // Build message. + $msg = '' . getTranslation('start_raid_now') . ':' . CR . CR; + $msg .= $text; } else { - $msg = '' . getTranslation('group_code_share') . ':' . CR; - $msg .= '' . getTranslation('no_active_raids_found') . ''; + $msg = '' . getTranslation('group_code_share') . ':' . CR; + $msg .= '' . getTranslation('no_active_raids_found') . ''; } // Send message. -send_message($update['message']['chat']['id'], $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); - -?> +send_message(create_chat_object([$update['message']['chat']['id']]), $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); diff --git a/mods/delete_scheduled_entry.php b/mods/delete_scheduled_entry.php deleted file mode 100644 index 012bf1d0..00000000 --- a/mods/delete_scheduled_entry.php +++ /dev/null @@ -1,52 +0,0 @@ -fetch(); - $msg = getTranslation('delete_scheduled_confirmation') . CR . CR; - $msg .= $pokemon['date_start'] . ' - ' . $pokemon['date_end'] . ':' . CR; - $msg .= getTranslation($pokemon['raid_level'] . 'stars') . ': '; - $msg .= get_local_pokemon_name($pokemon['pokedex_id'], $pokemon['pokemon_form_id']); - - $keys[] = [ - [ - 'text' => getTranslation('yes'), - 'callback_data' => $id . ':' . 'delete_scheduled_entry' . ':1' - ], - [ - 'text' => getTranslation('no'), - 'callback_data' => '0:pokedex_list_raids:0' - ], - ]; -} - -// Build callback message string. -$callback_response = 'OK'; - -// Telegram JSON array. -$tg_json = array(); - -// Answer callback. -$tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_response, true); - -// Edit message. -$tg_json[] = edit_message($update, $msg, $keys, false, true); - -// Telegram multicurl request. -curl_json_multi_request($tg_json); - -// Exit. -exit(); - -?> diff --git a/mods/edit_date.php b/mods/edit_date.php index 90acab81..d83985c6 100644 --- a/mods/edit_date.php +++ b/mods/edit_date.php @@ -7,85 +7,70 @@ //debug_log($data); // Check access. -bot_access_check($update, 'ex-raids'); +$botUser->accessCheck('create'); -// Set the id. -$id = $data['id']; -$gym_id = explode(',',$data['id'])[0]; - -// Get the argument. -$arg = $data['arg']; -$arg_data = explode(',', $arg); -$event_id = $arg_data[0]; -$raid_level = $arg_data[1]; -$pokemon_id = $arg_data[2]; -$raid_time = $arg_data[3]; +$raid_time = $data['t']; // Init empty keys array and set keys count. $keys = []; $keys_count = 2; -// Received: Year-Month-Day / 1970-01-01 / 2x "-" -if (substr_count($raid_time, '-') == 2) { - debug_log('Generating buttons for each hour of the day'); - // Buttons for each hour - for ($i = 0; $i <= 23; $i = $i + 1) { - // Create the keys. - $keys[] = array( - // Just show the time, no text - not everyone has a phone or tablet with a large screen... - 'text' => str_pad($i, 2, '0', STR_PAD_LEFT) . ':xx', - 'callback_data' => $id . ':edit_date:' . $arg . ' ' . str_pad($i, 2, '0', STR_PAD_LEFT) . '-' - ); - } - // Set keys count and message. - $keys_count = 4; - $msg = getTranslation('raid_select_hour'); -// Received: Year-Month-Day Hour- / 1970-01-01 00- / 3x "-" -} else if (substr_count($raid_time, '-') == 3) { - debug_log('Generating buttons for minute of the hour'); - $hour = explode(" ", $raid_time); - $hour = $hour[1]; - // Buttons for each minute - for ($i = 0; $i <= 45; $i = $i + 15) { - // Create the keys. - $keys[] = array( - // Just show the time, no text - not everyone has a phone or tablet with a large screen... - 'text' => substr($hour, 0, -1) . ':' . str_pad($i, 2, '0', STR_PAD_LEFT), - 'callback_data' => $id . ':edit_date:' . $arg . str_pad($i, 2, '0', STR_PAD_LEFT) . '-00' - ); - } - // Set keys count and message. - $keys_count = 4; - $msg = getTranslation('raid_select_start_time'); -// Received: Year-Month-Day Hour-Minute-Second / 1970-01-01 00-00-00 / 4x "-" -} else if (substr_count($raid_time, '-') == 4) { - debug_log('Received the following date for the raid: ' . $raid_time); - - // Format date, e.g 14 April 2019, 15:15h - $tz = $config->TIMEZONE; - $tz_raid_time = DateTimeImmutable::createFromFormat('Y-m-d H-i-s', $raid_time, new DateTimeZone($tz)); - $date_tz = $tz_raid_time->format('Y-m-d'); - $text_split = explode('-', $date_tz); - $text_day = $text_split[2]; - $text_month = getTranslation('month_' . $text_split[1]); - $text_year = $text_split[0]; - $time_tz = $tz_raid_time->format('H:i') . 'h'; - - // Raid time in UTC - $utc_raid_time = $tz_raid_time->setTimezone(new DateTimeZone('UTC')); - $utc_raid_time = $utc_raid_time->format('Y-m-d H-i-s'); - debug_log('Converting date to UTC to store in database'); - debug_log('UTC date for the raid: ' . $utc_raid_time); - debug_log('Waiting for confirmation to save the raid'); - - // Adding button to continue with next step in raid creation - $keys[] = array( - 'text' => getTranslation('next'), - 'callback_data' => $id . ':edit_time:' . $event_id . ','. $raid_level . ',' . $pokemon_id . ',' . $utc_raid_time . ',X,0' - ); - - // Set message. - $msg = getTranslation('start_date_time') . ':' . CR .'' . $text_day . SP . $text_month . SP . $text_year . ',' . SP . $time_tz . ''; +$buttonData = $data; +// Received: YearMonthDay / 19700101 +if (strlen($raid_time) == 8) { + debug_log('Generating buttons for each hour of the day'); + // Buttons for each hour + $buttonData[0] = 'edit_date'; + for ($i = 0; $i <= 23; $i = $i + 1) { + $buttonData['t'] = $data['t'] . str_pad($i, 2, '0', STR_PAD_LEFT); + // Create the keys. + $keys[] = button(str_pad($i, 2, '0', STR_PAD_LEFT) . ':xx', $buttonData); + } + // Set keys count and message. + $keys_count = 4; + $msg = getTranslation('raid_select_hour'); +// Received: YearMonthDayHour / 1970010100 +} else if (strlen($raid_time) == 10) { + debug_log('Generating buttons for minute of the hour'); + $hour = substr($raid_time,8,2); + // Buttons for each minute + $buttonData[0] = 'edit_date'; + for ($i = 0; $i <= 45; $i = $i + 15) { + $buttonData['t'] = $data['t'] . str_pad($i, 2, '0', STR_PAD_LEFT); + // Create the keys. + $keys[] = button($hour . ':' . str_pad($i, 2, '0', STR_PAD_LEFT), $buttonData); + } + // Set keys count and message. + $keys_count = 4; + $msg = getTranslation('raid_select_start_time'); +// Received: YearMonthDayHourMinute / 197001010000 +} else if (strlen($raid_time) == 12) { + debug_log('Received the following date for the raid: ' . $raid_time); + + // Format date, e.g 14 April 2019, 15:15h + $tz = $config->TIMEZONE; + $tz_raid_time = DateTimeImmutable::createFromFormat('YmdHi', $raid_time, new DateTimeZone($tz)); + $date_tz = $tz_raid_time->format('Y-m-d'); + $text_split = explode('-', $date_tz); + $text_day = $text_split[2]; + $text_month = getTranslation('month_' . $text_split[1]); + $text_year = $text_split[0]; + $time_tz = $tz_raid_time->format('H:i') . 'h'; + + // Raid time in UTC + $utc_raid_time = $tz_raid_time->setTimezone(new DateTimeZone('UTC')); + $utc_raid_time = $utc_raid_time->format('YmdHi'); + debug_log('Converting date to UTC to store in database'); + debug_log('UTC date for the raid: ' . $utc_raid_time); + debug_log('Waiting for confirmation to save the raid'); + + // Adding button to continue with next step in raid creation + $buttonData[0] = 'edit_time'; + $buttonData['t'] = $utc_raid_time; + $keys[] = button(getTranslation('next'), $buttonData); + + // Set message. + $msg = getTranslation('start_date_time') . ':' . CR .'' . $text_day . SP . $text_month . SP . $text_year . ',' . SP . $time_tz . ''; } // Get the inline key array. @@ -96,13 +81,13 @@ // Back key id, action and arg if(substr_count($raid_time, '-') == 1 || substr_count($raid_time, '-') == 4) { - $back_id = $id; - $back_action = 'edit_starttime'; - $back_arg = $arg; - $nav_keys[] = universal_inner_key($nav_keys, $back_id, $back_action, $back_arg, getTranslation('back')); + $backData = $data; + $backData[0] = 'edit_starttime'; + unset($backData['t']); + $nav_keys[] = button(getTranslation('back'), $backData); } -$nav_keys[] = universal_inner_key($nav_keys, $gym_id, 'exit', '2', getTranslation('abort')); +$nav_keys[] = button(getTranslation('abort'), 'exit'); $nav_keys = inline_key_array($nav_keys, 2); // Merge keys. @@ -122,6 +107,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/edit_event.php b/mods/edit_event.php index 256da3fe..bd1caae8 100644 --- a/mods/edit_event.php +++ b/mods/edit_event.php @@ -1,46 +1,31 @@ accessCheck('ex-raids', true); +// Check access - user must be admin for raid event creation +$admin_access[1] = $botUser->accessCheck('event-raids', true); // Get the keys. -$keys = keys_event($gym_id_plus_letter, "edit_event_raidlevel"); - -// No keys found. -if (!$keys) { - $keys = [ - [ - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ] - ]; -} else { - // Back key id, action and arg - $back_id_arg = explode(',', $gym_id_plus_letter); - $back_id = $back_id_arg[1]; - $back_action = 'edit_raidlevel'; - $back_arg = $back_id_arg[0]; - - // Add navigation keys. - $nav_keys = []; - $nav_keys[] = universal_inner_key($nav_keys, $back_id, $back_action, $back_arg, getTranslation('back')); - $nav_keys[] = universal_inner_key($nav_keys, $back_arg, 'exit', '2', getTranslation('abort')); - $nav_keys = inline_key_array($nav_keys, 2); - - // Merge keys. - $keys = array_merge($keys, $nav_keys); -} +$keys = keys_event($data, 'edit_event_raidlevel', $admin_access); + +$backData = $data; +$backData[0] = 'edit_raidlevel'; + +// Add navigation keys. +$keys[] = [ + button(getTranslation('back'), $backData), + button(getTranslation('abort'), 'exit') +]; + // Build callback message string. $callback_response = 'OK'; @@ -56,6 +41,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); \ No newline at end of file diff --git a/mods/edit_event_note.php b/mods/edit_event_note.php index 5ceb9fba..c24ae268 100644 --- a/mods/edit_event_note.php +++ b/mods/edit_event_note.php @@ -1,68 +1,61 @@ $raid_id,"old_message_id"=>$update['callback_query']['message']['message_id'])); // Save the raid id and the message id to db so we can delete it later - $handler = "save_event_note"; // call for mods/save_event_note.php after user posts the answer - - my_query("INSERT INTO user_input SET user_id='{$userid}', modifiers='{$modifiers}', handler='{$handler}'"); -}elseif($mode == "cancel") { - my_query("DELETE FROM user_input WHERE user_id='{$userid}'"); - $data['arg'] = 0; - require_once("edit_save.php"); -}else { - if($raid['event'] == EVENT_ID_EX) { - $event_name = getTranslation('Xstars'); - }else { - $q = my_query("SELECT * FROM events WHERE id='{$raid['event']}'"); - $res = $q->fetch(); - $event_name = $res['name']; - } +if($mode == 'e') { + $msg.= getTranslation('event_note_edit') . ': '; + + // Create an entry to user_input table + $modifiers = json_encode(array('id' => $raid_id, 'old_message_id' => $update['callback_query']['message']['message_id'])); // Save the raid id and the message id to db so we can delete it later + $handler = 'save_event_note'; // call for mods/save_event_note.php after user posts the answer - $msg.= getTranslation("event") . ": ".$event_name."".CR; - $msg.= getTranslation("event_add_note_description"); - - // Create an entry to user_input table - $modifiers = json_encode(array("id"=>$raid_id,"old_message_id"=>$update['callback_query']['message']['message_id'])); // Save the raid id and the message id to db so we can delete it later - $handler = "save_event_note"; // call for mods/save_event_note.php after user posts the answer - - my_query("INSERT INTO user_input SET user_id='{$userid}', modifiers='{$modifiers}', handler='{$handler}'"); + my_query('INSERT INTO user_input SET user_id=:userId, modifiers=:modifiers, handler=:handler', ['userId' => $userid, 'modifiers' => $modifiers, 'handler' => $handler]); +}elseif($mode == 'c') { + my_query('DELETE FROM user_input WHERE user_id = ?', [$userid]); + require_once('edit_save.php'); + exit(); +}else { + if($raid['event'] == EVENT_ID_EX) { + $event_name = getTranslation('Xstars'); + }else { + $q = my_query('SELECT name FROM events WHERE id = ?', [$raid['event']]); + $res = $q->fetch(); + $event_name = $res['name']; + } + + $msg.= getTranslation('event') . ': ' . $event_name . '' . CR; + $msg.= getTranslation('event_add_note_description'); + + // Create an entry to user_input table + $modifiers = json_encode(array('id' => $raid_id, 'old_message_id' => $update['callback_query']['message']['message_id'])); // Save the raid id and the message id to db so we can delete it later + $handler = 'save_event_note'; // call for mods/save_event_note.php after user posts the answer + + my_query('INSERT INTO user_input SET user_id = :userId, modifiers = :modifiers, handler = :handler', ['userId' => $userid, 'modifiers' => $modifiers, 'handler' => $handler]); } -$keys[] = [ - [ - 'text' => getTranslation('cancel'), - 'callback_data' => $raid_id . ':edit_event_note:'. $arg[0] . ',cancel' - ] - ]; +$keys[][] = button(getTranslation('cancel'), ['edit_event_note', 'r' => $raid_id, 'm' => 'c']); // Answer callback. answerCallbackQuery($update['callback_query']['id'], $callback_response); // Edit message. edit_message($update, $msg, $keys, false); - -exit(); \ No newline at end of file diff --git a/mods/edit_event_raidlevel.php b/mods/edit_event_raidlevel.php index f952d0e3..df4b9af5 100644 --- a/mods/edit_event_raidlevel.php +++ b/mods/edit_event_raidlevel.php @@ -1,70 +1,51 @@ accessCheck('ex-raids', true); +// Check access - user must be admin for raid event creation +$admin_access[1] = $botUser->accessCheck('event-raids', true); // Get the keys. -$keys = raid_edit_raidlevel_keys($gym_id, $gym_first_letter, $admin_access, $event_id); - -// No keys found. -if (!$keys) { - // Create the keys. - $keys = [ - [ - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ] - ]; -} else { - // Add navigation keys. - $nav_keys = []; - $nav_keys[] = universal_inner_key($nav_keys, $back_id, $back_action, $back_arg, getTranslation('back')); - $nav_keys[] = universal_inner_key($nav_keys, $gym_id, 'exit', '2', getTranslation('abort')); - $nav_keys = inline_key_array($nav_keys, 2); - // Merge keys. - $keys = array_merge($keys, $nav_keys); -} -// Get event info -$q = my_query("SELECT name, description FROM events WHERE id='{$event_id}' LIMIT 1"); +$keys = raid_edit_raidlevel_keys($data, $admin_access, $event_id); + +$backData = $data; +$backData[0] = 'edit_event'; +// Add navigation keys. +$keys[] = [ + button(getTranslation('back'), $backData), + button(getTranslation('abort'), 'exit') +]; + +// Get event info +$q = my_query('SELECT name, description FROM events WHERE id = ? LIMIT 1', [$event_id]); $rs = $q->fetch(); // Build message. -if($event_id == 'X') { - $msg = "".getTranslation('Xstars')."".CR; +if($event_id == EVENT_ID_EX) { + $msg = '' . getTranslation(RAID_ID_EX . 'stars') . '' . CR; }else { - $msg = "{$rs['name']}".CR."{$rs['description']}".CR; + $msg = '' . $rs['name'] . '' . CR . $rs['description'] . CR; } -$msg.= getTranslation('create_raid') . ': ' . (($gym['address']=="") ? $gym['gym_name'] : $gym['address']) . ''; +$msg .= getTranslation('create_raid') . ': ' . (($gym['address']=='') ? $gym['gym_name'] : $gym['address']) . ''; // Build callback message string. $callback_response = getTranslation('gym_saved'); @@ -77,7 +58,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); - diff --git a/mods/edit_pokemon.php b/mods/edit_pokemon.php index 28d21453..af480315 100644 --- a/mods/edit_pokemon.php +++ b/mods/edit_pokemon.php @@ -1,4 +1,5 @@ accessCheck('create'); // Set the raid level and event. -$event_id = $arg_data[0]; -$raid_level = $arg_data[1]; +$eventId = $data['e'] ?? NULL; +$raidLevel = $data['rl']; // Check if we are creating an event -if($event_id != "N") { - // If yes, go to date selection - $action = "edit_time"; +if($eventId != NULL) { + // If yes, go to date selection + $action = "edit_time"; }else { - // If not, select start time - $action = "edit_starttime"; + // If not, select start time + $action = "edit_starttime"; } // Get the keys. -$keys = pokemon_keys($gym_id_plus_letter, $raid_level, "edit_starttime", $event_id); - -// No keys found. -if (!$keys) { - $keys = [ - [ - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ] - ]; -} else { - if($event_id == "N") { - $back_id_arg = explode(',', $gym_id_plus_letter); - $back_id = $back_id_arg[1]; - $back_action = 'edit_raidlevel'; - $back_arg = $back_id_arg[0]; - }else { - $back_id = $gym_id_plus_letter; - $back_action = 'edit_event_raidlevel'; - $back_arg = $event_id; - } +$keys = pokemon_keys($data, $raidLevel, 'edit_starttime', $eventId); - // Add navigation keys. - $nav_keys = []; - $nav_keys[] = universal_inner_key($nav_keys, $back_id, $back_action, $back_arg, getTranslation('back')); - $nav_keys[] = universal_inner_key($nav_keys, $back_arg, 'exit', '2', getTranslation('abort')); - $nav_keys = inline_key_array($nav_keys, 2); +$back_action = ($eventId == NULL) ? 'edit_raidlevel' : 'edit_event_raidlevel'; - // Merge keys. - $keys = array_merge($keys, $nav_keys); -} +// Add navigation keys. +$backData = $data; +$backData[0] = $back_action; +$keys[] = [ + button(getTranslation('back'), $backData), + button(getTranslation('abort'), 'exit') +]; // Build callback message string. $callback_response = 'OK'; @@ -73,13 +47,10 @@ // Edit the message. if (isset($update['callback_query']['inline_message_id'])) { - $tg_json[] = editMessageText($update['callback_query']['inline_message_id'], getTranslation('select_raid_boss') . ':', $keys, NULL, false, true); + $tg_json[] = editMessageText($update['callback_query']['inline_message_id'], getTranslation('select_raid_boss') . ':', $keys, NULL, false, true); } else { - $tg_json[] = editMessageText($update['callback_query']['message']['message_id'], getTranslation('select_raid_boss') . ':', $keys, $update['callback_query']['message']['chat']['id'], $keys, true); + $tg_json[] = editMessageText($update['callback_query']['message']['message_id'], getTranslation('select_raid_boss') . ':', $keys, $update['callback_query']['message']['chat']['id'], $keys, true); } // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/edit_raidlevel.php b/mods/edit_raidlevel.php index 71015904..8b00281c 100644 --- a/mods/edit_raidlevel.php +++ b/mods/edit_raidlevel.php @@ -1,108 +1,105 @@ accessCheck('create'); -// Get gym data via ID in arg -$gym_id = $data['arg']; +$gym_id = $data['g']; $gym = get_gym($gym_id); -// Back key id, action and arg -if(substr($data['id'],0,2) == 'gl') { - $back_id = substr($data['id'],2); - $back_action = 'raid_by_gym_letter'; - $back_arg = '0'; -}else { - $back_id = 0; - $back_action = 'raid_by_gym'; - $back_arg = $data['id']; +$showBackButton = true; +if(isset($data['z'])) { + $showBackButton = false; + unset($data['z']); } -$gym_first_letter = $back_arg; // Telegram JSON array. $tg_json = array(); +//Initialize admin rights table [ ex-raid , raid-event ] +$admin_access = [false,false]; +// Check access - user must be admin for raid_level X +$admin_access[0] = $botUser->accessCheck('ex-raids', true); +// Check access - user must be admin for raid event creation +$admin_access[1] = $botUser->accessCheck('event-raids', true); + // Active raid? $duplicate_id = active_raid_duplication_check($gym_id); if ($duplicate_id > 0) { - $keys = []; + $keys = []; + // In case gym has already a normal raid saved to it and user has privileges to create an event raid, create a special menu + if($admin_access[0] == true || $admin_access[1] == true) { + $msg = EMOJI_WARN . SP . getTranslation('raid_already_exists') . CR; + $msg .= getTranslation('inspect_raid_or_create_event') . ':'; + + $eventData = $backData = $data; + $eventData[0] = 'edit_event'; + $backData[0] = 'gymMenu'; + $backData['stage'] = 2; + $backData['a'] = 'create'; + $keys[0][0] = button(getTranslation('saved_raid'), ['raids_list', 'r' => $duplicate_id]); + $keys[1][0] = button(getTranslation('create_event_raid'), $eventData); + $keys[2][0] = button(getTranslation('back'), $backData); + $keys[2][1] = button(getTranslation('abort'), 'exit'); + } else { $raid_id = $duplicate_id; $raid = get_raid($raid_id); $msg = EMOJI_WARN . SP . getTranslation('raid_already_exists') . SP . EMOJI_WARN . CR . show_raid_poll_small($raid); - - // Check if the raid was already shared. - $rs_share = my_query( - " - SELECT COUNT(*) AS raid_count - FROM cleanup - WHERE raid_id = '{$raid_id}' - " - ); - - $shared = $rs_share->fetch(); - - // Add keys for sharing the raid. - if($shared['raid_count'] == 0) { - $keys = share_keys($raid_id, 'raid_share', $update); - - // Exit key - $empty_exit_key = []; - $key_exit = universal_key($empty_exit_key, '0', 'exit', '0', getTranslation('abort')); - $keys = array_merge($keys, $key_exit); + $keys = share_keys($raid_id, 'raid_share', $update, $raid['level']); + if($botUser->raidaccessCheck($raid['id'], 'pokemon', true)) { + $keys[][] = button(getTranslation('update_pokemon'), ['raid_edit_poke', 'r' => $raid['id'], 'rl' => $raid['level']]); + } + if($botUser->raidaccessCheck($raid['id'], 'delete', true)) { + $keys[][] = button(getTranslation('delete'), ['raids_delete', 'r' => $raid['id']]); } + $keys[][] = button(getTranslation('abort'), 'exit'); + } - // Answer callback. - $tg_json[] = answerCallbackQuery($update['callback_query']['id'], getTranslation('raid_already_exists'), true); + // Answer callback. + $tg_json[] = answerCallbackQuery($update['callback_query']['id'], getTranslation('raid_already_exists'), true); - // Edit the message. - $tg_json[] = edit_message($update, $msg, $keys, false, true); + // Edit the message. + $tg_json[] = edit_message($update, $msg, $keys, false, true); - // Telegram multicurl request. - curl_json_multi_request($tg_json); + // Telegram multicurl request. + curl_json_multi_request($tg_json); - // Exit. - exit(); + // Exit. + exit(); } -//Initialize admin rights table [ ex-raid , raid-event ] -$admin_access = [false,false]; -// Check access - user must be admin for raid_level X -$admin_access[0] = bot_access_check($update, 'ex-raids', true); -// Check access - user must be admin for raid event creation -$admin_access[1] = bot_access_check($update, 'event-raids', true); - +$eliteId = active_raid_duplication_check($gym_id, 9); +$excludeElite = $eliteId == 0 ? false : true; // Get the keys. -$keys = raid_edit_raidlevel_keys($gym_id, $gym_first_letter, $admin_access); - -// No keys found. -if (!$keys) { - // Create the keys. - $keys = [ - [ - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ] - ]; -} else { - // Add navigation keys. - $nav_keys = []; - $nav_keys[] = universal_inner_key($nav_keys, $back_id, $back_action, $back_arg, getTranslation('back')); - $nav_keys[] = universal_inner_key($nav_keys, $gym_id, 'exit', '2', getTranslation('abort')); - $nav_keys = inline_key_array($nav_keys, 2); - // Merge keys. - $keys = array_merge($keys, $nav_keys); +$keys = raid_edit_raidlevel_keys($data, $admin_access, false, $excludeElite); + +if($eliteId > 0) { + $keys[][] = button(getTranslation('saved_raid'), ['raids_list', 'r' => $eliteId]); } +$lastRow = []; +if($showBackButton) { + $backData = $data; + $backData[0] = 'gymMenu'; + $backData['a'] = 'create'; + $backData['stage'] = 2; + // Add navigation keys. + $lastRow[] = button(getTranslation('back'), $backData); +} +$lastRow[] = button(getTranslation('abort'), 'exit'); +$keys[] = $lastRow; + // Build message. -$msg = getTranslation('create_raid') . ': ' . (($gym['address']=="") ? $gym['gym_name'] : $gym['address']) . ''; +$msg = getTranslation('create_raid') . ': ' . (($gym['address'] == '') ? $gym['gym_name'] : $gym['address']) . ''; // Build callback message string. $callback_response = getTranslation('gym_saved'); @@ -115,7 +112,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); - diff --git a/mods/edit_save.php b/mods/edit_save.php index 0c55b065..56d6b02b 100644 --- a/mods/edit_save.php +++ b/mods/edit_save.php @@ -1,170 +1,78 @@ accessCheck('create'); + +// Set raid id +$id = $data['r']; // Set the user id. $userid = $update['callback_query']['from']['id']; -// Update only if time is not equal to RAID_DURATION -if($arg != $config->RAID_DURATION && $arg != 0) { - - // Build query. - my_query( - " - UPDATE raids - SET end_time = DATE_ADD(start_time, INTERVAL {$arg} MINUTE) - WHERE id = {$id} - " - ); -} - -// Fast forward to raid sharing. -if(substr_count($data['id'], ',') == 1) { - // Write to log. - debug_log('Doing a fast forward now!'); - debug_log('Changing data array first...'); - - // Reset data array - $data = []; - $data['id'] = $id; - $data['action'] = 'raid_share'; - $data['arg'] = $chat; - - // Write to log. - debug_log($data, '* NEW DATA= '); - - // Set module path by sent action name. - $module = ROOT_PATH . '/mods/raid_share.php'; - - // Write module to log. - debug_log($module); - - // Check if the module file exists. - if (file_exists($module)) { - // Dynamically include module file and exit. - include_once($module); - exit(); - } else { - info_log($module, 'Error! Fast forward failed as file does not exist:'); - exit(); - } +// Update raid end time +if(isset($data['d'])) { + my_query(' + UPDATE raids + SET end_time = DATE_ADD(start_time, INTERVAL :duration MINUTE) + WHERE id = :id + ', [ + 'id' => $id, + 'duration' => $data['d'] + ] + ); } // Telegram JSON array. $tg_json = array(); -// Init keys. -$keys = []; - -// Add delete to keys. -$keys = [ - [ - [ - 'text' => getTranslation('delete'), - 'callback_data' => $id . ':raids_delete:0' - ] - ] -]; +// Add delete and done to keys. +$keys[][] = button(getTranslation('delete'), ['raids_delete', 'r' => $id]); +$keys[][] = button(getTranslation('done'), ['exit', 'd' => 1]); // Check access level prior allowing to change raid time -$admin_access = bot_access_check($update, 'raid-duration', true); -if($admin_access) { - // Add time change to keys. - $keys_time = [ - [ - [ - 'text' => getTranslation('change_raid_duration'), - 'callback_data' => $id . ':edit_time:0,0,0,0,more,1' - ] - ] - ]; - $keys = array_merge($keys, $keys_time); +if($botUser->accessCheck('raid-duration', true)) { + // Add time change to keys. + $keys[][] = button(getTranslation('change_raid_duration'), ['edit_time', 'r' => $id, 'o' => 'm']); } // Get raid times. -$raid = get_raid($data['id']); +$raid = get_raid($id); // Get raid level. $raid_level = $raid['level']; -$const = 'SHARE_CHATS_LEVEL_' . $raid_level; -$const_chats = $config->{$const}; - -// Debug. -//debug_log($const,'CONSTANT NAME:'); -//debug_log(constant($const),'CONSTANT VALUE:'); - -// Special sharing keys for raid level? -if(!empty($const_chats)) { - $chats = $const_chats; - debug_log('Special sharing keys detected for raid level ' . $raid_level); -} else { - $chats = ''; -} -if($raid['event']!==NULL) { - if($raid['event_note']==NULL) { - $event_button_text = getTranslation("event_note_add"); - }else { - $event_button_text = getTranslation("event_note_edit"); - } - $keys_edit_event_note = [ - [ - [ - 'text' => $event_button_text, - 'callback_data' => $id . ':edit_event_note:0' - ] - ] - ]; - $keys = array_merge($keys, $keys_edit_event_note); +if($raid['event'] !== NULL) { + $event_button_text = ($raid['event_note'] == NULL) ? getTranslation("event_note_add") : getTranslation("event_note_edit"); + $keys[][] = button($event_button_text, ['edit_event_note', 'r' => $id, 'm' => 'e']); } // Add keys to share. -$keys_share = share_keys($id, 'raid_share', $update, $chats); +$keys_share = share_keys($id, 'raid_share', $update, $raid_level); $keys = array_merge($keys, $keys_share); // Build message string. $msg = ''; $msg .= getTranslation('raid_saved') . CR; -$msg .= show_raid_poll_small($raid, false) . CR; +$msg .= show_raid_poll_small($raid) . CR; // User_id tag. $user_id_tag = '#' . $update['callback_query']['from']['id']; // Gym Name -if(!empty($raid['gym_name']) && ($raid['gym_name'] != $user_id_tag)) { -$msg .= getTranslation('set_gym_team') . CR2; -} else { - $msg .= getTranslation('set_gym_name_and_team') . CR2; - $msg .= getTranslation('set_gym_name_command') . CR; +if(!empty($raid['gym_name']) && ($raid['gym_name'] == $user_id_tag)) { + $msg .= getTranslation('set_gym_name_and_team') . CR2; + $msg .= getTranslation('set_gym_name_command') . CR; } -$msg .= getTranslation('set_gym_team_command'); // Build callback message string. -if($arg == 0) { - $callback_response = 'OK'; -}else { - $callback_response = getTranslation('end_time') . ' ' . $data['arg'] . ' ' . getTranslation('minutes'); -} +$callback_response = 'OK'; // Answer callback. $tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_response, true); @@ -174,6 +82,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/edit_scheduled_entry.php b/mods/edit_scheduled_entry.php new file mode 100644 index 00000000..6ef5e98d --- /dev/null +++ b/mods/edit_scheduled_entry.php @@ -0,0 +1,70 @@ +accessCheck('pokedex'); +$id = $data['i']; + +$query = my_query('SELECT pokedex_id, pokemon_form_id, date_start, date_end, raid_level, disabled FROM raid_bosses WHERE id = ? LIMIT 1', [$id]); +$pokemon = $query->fetch(); +if(isset($data['s']) && $data['s'] == 1) { + my_query('UPDATE raid_bosses SET disabled = NOT disabled WHERE id = ?', [$id]); + $pokemon['disabled'] = ($pokemon['disabled'] == 1) ? 0 : 1; +} +if(isset($data['s']) && $data['s'] == 2) { + $msg = getTranslation('delete_scheduled_confirmation') . CR . CR; + $msg .= $pokemon['date_start'] . ' - ' . $pokemon['date_end'] . ':' . CR; + $msg .= getTranslation($pokemon['raid_level'] . 'stars') . ': '; + $msg .= get_local_pokemon_name($pokemon['pokedex_id'], $pokemon['pokemon_form_id']) . CR; + $msg .= '' . ($pokemon['disabled'] ? getTranslation('disabled') : getTranslation('enabled')) .''; + $keys[0][0] = button(getTranslation('yes'), ['edit_scheduled_entry', 'i' => $id, 's' => 3]); + $keys[0][1] = button(getTranslation('no'), ['edit_scheduled_entry', 'i' => $id]); + // Build callback message string. + $callback_response = 'OK'; + + // Telegram JSON array. + $tg_json = array(); + + // Answer callback. + $tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_response, true); + + // Edit message. + $tg_json[] = edit_message($update, $msg, $keys, false, true); + + // Telegram multicurl request. + curl_json_multi_request($tg_json); + exit(); +} +if(isset($data['s']) && $data['s'] == 3) { + my_query('DELETE FROM raid_bosses WHERE id = ?', [$id]); + include(ROOT_PATH . '/mods/pokedex_list_raids.php'); + exit(); +} +$msg = getTranslation('edit_scheduled_entry') . ':' . CR . CR; +$msg .= EMOJI_CLOCK . SP . $pokemon['date_start'] . ' - ' . $pokemon['date_end'] . CR; +$msg .= getTranslation($pokemon['raid_level'] . 'stars') . ': '; +$msg .= get_local_pokemon_name($pokemon['pokedex_id'], $pokemon['pokemon_form_id']) . CR; +$msg .= '' . ($pokemon['disabled'] ? getTranslation('disabled') : getTranslation('enabled')) .''; + +$keys[0][] = button( + ($pokemon['disabled'] ? getTranslation('enable') : getTranslation('disable')), + ['edit_scheduled_entry', 'i' => $id, 's' => 1] +); +$keys[1][] = button(getTranslation('delete'), ['edit_scheduled_entry', 'i' => $id, 's' => 2]); +$keys[2][] = button(getTranslation('back'), 'pokedex_list_raids'); + +// Build callback message string. +$callback_response = 'OK'; + +// Telegram JSON array. +$tg_json = array(); + +// Answer callback. +$tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_response, true); + +// Edit message. +$tg_json[] = edit_message($update, $msg, $keys, false, true); + +// Telegram multicurl request. +curl_json_multi_request($tg_json); diff --git a/mods/edit_starttime.php b/mods/edit_starttime.php index 8de3c303..f573c0bf 100644 --- a/mods/edit_starttime.php +++ b/mods/edit_starttime.php @@ -7,174 +7,145 @@ //debug_log($data); // Check access. -bot_access_check($update, 'create'); +$botUser->accessCheck('create'); // Get the argument. -$arg_data = explode(",", $data['arg']); -$event_id = $arg_data[0]; -$raid_level = $arg_data[1]; -$pokemon_id = $arg_data[2]; -$arg = ""; -// Check for options. -if (isset($arg_data[3])) -{ - // Switch time display ( min / clock ) - $arg = $arg_data[3]; -} +$event_id = $data['e'] ?? NULL; +$raid_level = $data['rl']; +$arg = $data['o'] ?? ''; + // Set the id. -$gym_id_plus_letter = $data['id']; -$gym_id = explode(',', $data['id'])[0]; +$gym_id = $data['g']; // Are we creating an event? -if($event_id != 'N') { - // Init empty keys array. - $keys = []; - - // Current time from the user - // We let the user pick the raid date and time and convert to UTC afterwards in edit_date.php - $tz = $config->TIMEZONE; - $today = new DateTimeImmutable('now', new DateTimeZone($tz)); - - // Next 14 days. - for ($d = 0; $d <= 14; $d = $d + 1) { - // Add day to today. - $today_plus_d = $today->add(new DateInterval("P".$d."D")); - - // Format date, e.g 14 April 2019 - $date_tz = $today_plus_d->format('Y-m-d'); - $text_split = explode('-', $date_tz); - $text_day = $text_split[2]; - $text_month = getTranslation('month_' . $text_split[1]); - $text_year = $text_split[0]; - - // Add keys. - $cb_date = $today_plus_d->format('Y-m-d'); - $keys[] = array( - 'text' => $text_day . SP . $text_month . SP . $text_year, - 'callback_data' => $gym_id_plus_letter . ':edit_date:' . $event_id . ',' . $raid_level . ',' . $pokemon_id . ',' . $cb_date - ); - } - - // Get the inline key array. - $keys = inline_key_array($keys, 2); +if($event_id != NULL or $raid_level == 9) { + // Init empty keys array. + $keys = []; + + // Current time from the user + // We let the user pick the raid date and time and convert to UTC afterwards in edit_date.php + $tz = $config->TIMEZONE; + $today = new DateTimeImmutable('now', new DateTimeZone($tz)); + + // Create date buttons. Two days for Elite Raids, 15 days for EX raids. + $days = ($raid_level == 9) ? 1 : 14; + unset($data['o']); + $buttonData = $data; + $buttonData[0] = 'edit_date'; + // Drop these from callback_data string, these are no longer needed + unset($buttonData['fl']); + unset($buttonData['ga']); + for ($d = 0; $d <= $days; $d++) { + // Add day to today. + $today_plus_d = $today->add(new DateInterval("P".$d."D")); + + // Format date, e.g 14 April 2019 + $date_tz = $today_plus_d->format('Ymd'); + $text_day = $today_plus_d->format('d'); + $text_month = getTranslation('month_' . $today_plus_d->format('m')); + $text_year = $today_plus_d->format('Y'); + + // Add keys. + $buttonData['t'] = $date_tz; + $keys[] = button($text_day . SP . $text_month . SP . $text_year, $buttonData); + } + + // Get the inline key array. + $keys = inline_key_array($keys, 2); // Not creating an event } else { - if ($arg != "min" && $arg != "clock") { - // Get default raid duration style from config - if ($config->RAID_DURATION_CLOCK_STYLE) { - $arg = "clock"; - } else { - $arg = "min"; - } - } - - // Init empty keys array. - $keys = []; - - // Now - $now = utcnow(); - - if ($arg == "min") { - // Set switch view. - $switch_text = getTranslation('raid_starts_when_clocktime_view'); - $switch_view = "clock"; - $key_count = 5; - - for ($i = 1; $i <= $config->RAID_EGG_DURATION; $i = $i + 1) { - // Create new DateTime object, add minutes and convert back to string. - $now_plus_i = new DateTime($now, new DateTimeZone('UTC')); - $now_plus_i->add(new DateInterval('PT'.$i.'M')); - $now_plus_i = $now_plus_i->format("Y-m-d H:i:s"); - // Create the keys. - $keys[] = array( - // Just show the time, no text - not everyone has a phone or tablet with a large screen... - 'text' => floor($i / 60) . ':' . str_pad($i % 60, 2, '0', STR_PAD_LEFT), - 'callback_data' => $gym_id_plus_letter . ':edit_time:' . $event_id . ',' . $raid_level . ',' . $pokemon_id . ',' . utctime($now_plus_i,"H-i") - ); - } + if ($arg != "min" && $arg != "clock") { + // Get default raid duration style from config + if ($config->RAID_DURATION_CLOCK_STYLE) { + $arg = "clock"; } else { - // Set switch view. - $switch_text = getTranslation('raid_starts_when_minutes_view'); - $switch_view = "min"; - // Small screen fix - $key_count = 4; - - for ($i = 1; $i <= $config->RAID_EGG_DURATION; $i = $i + 1) { - // Create new DateTime object, add minutes and convert back to string. - $now_plus_i = new DateTime($now, new DateTimeZone('UTC')); - $now_plus_i->add(new DateInterval('PT'.$i.'M')); - $now_plus_i = $now_plus_i->format("Y-m-d H:i:s"); - // Create the keys. - $keys[] = array( - // Just show the time, no text - not everyone has a phone or tablet with a large screen... - 'text' => dt2time($now_plus_i), - 'callback_data' => $gym_id_plus_letter . ':edit_time:' . $event_id . ',' . $raid_level . ',' . $pokemon_id . ',' . utctime($now_plus_i,"H-i") - ); - } + $arg = "min"; } + } + + // Init empty keys array. + $keys = []; + + // Now + $now = utcnow(); + + // Copy received callbackData to new variable that we can edit + $buttonData = $data; + $buttonData[0] = 'edit_time'; + if ($arg == "min") { + // Set switch view. + $switch_text = getTranslation('raid_starts_when_clocktime_view'); + $switch_view = "clock"; + $key_count = 5; + + } else { + // Set switch view. + $switch_text = getTranslation('raid_starts_when_minutes_view'); + $switch_view = "min"; + // Small screen fix + $key_count = 4; + } + $now_plus_i = new DateTime($now, new DateTimeZone('UTC')); + for ($i = 1; $i <= $config->RAID_EGG_DURATION; $i = $i + 1) { + $now_plus_i->add(new DateInterval('PT1M')); + $buttonData['t'] = $now_plus_i->format("YmdHi"); + if ($arg == 'min') + $buttonText = floor($i / 60) . ':' . str_pad($i % 60, 2, '0', STR_PAD_LEFT); + else + $buttonText = dt2time($now_plus_i->format('Y-m-d H:i:s')); - // Get the inline key array. - $keys = inline_key_array($keys, $key_count); - - // Init empty keys other options array. - $keys_opt = []; - - // Raid already running - $keys_opt[] = array( - 'text' => getTranslation('is_raid_active'), - 'callback_data' => $gym_id_plus_letter . ':edit_time:' . $event_id . ',' . $raid_level . ',' . $pokemon_id . ',' . utctime($now,"H-i").",more,0" - ); - - // Switch view: clocktime / minutes until start - $keys_opt[] = array( - 'text' => $switch_text, - 'callback_data' => $gym_id_plus_letter . ':edit_starttime:' . $event_id . ',' . $raid_level . ',' . $pokemon_id . ',' . $switch_view - ); - - // Get the inline key array. - $keys_opt = inline_key_array($keys_opt, 2); - - // Merge keys - $keys = array_merge($keys, $keys_opt); - - // Write to log. - debug_log($keys); + // Create the keys. + $keys[] = button($buttonText, $buttonData); + } + + // Get the inline key array. + $keys = inline_key_array($keys, $key_count); + + // Init empty keys other options array. + $keys_opt = []; + $keyData = $data; + $keyData[0] = 'edit_time'; + $keyData['o'] = 'm'; + $keyData['t'] = utctime($now,"YmdHi"); + // Raid already running + $keys_opt[] = button(getTranslation('is_raid_active'), $keyData); + $keyData[0] = 'edit_starttime'; + $keyData['o'] = $switch_view; + unset($keyData['t']); + // Switch view: clocktime / minutes until start + $keys_opt[] = button($switch_text, $keyData); + + // Get the inline key array. + $keys_opt = inline_key_array($keys_opt, 2); + + // Merge keys + $keys = array_merge($keys, $keys_opt); + + // Write to log. + debug_log($keys); } // No keys found. if (!$keys) { - // Create the keys. - $keys = [ - [ - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ] - ]; + // Create the keys. + $keys[][] = button(getTranslation('abort'), 'exit'); } else { - // Back key id, action and arg - $back_id = $gym_id_plus_letter; - $back_action = 'edit_pokemon'; - $back_arg = $event_id . ',' . $raid_level; - - // Add navigation keys. - $nav_keys = []; - $nav_keys[] = universal_inner_key($nav_keys, $back_id, $back_action, $back_arg, getTranslation('back')); - $nav_keys[] = universal_inner_key($nav_keys, $gym_id, 'exit', '2', getTranslation('abort')); - $nav_keys = inline_key_array($nav_keys, 2); - - // Merge keys. - $keys = array_merge($keys, $nav_keys); + $backData = $data; + $backData[0] = 'edit_pokemon'; + // Add navigation keys. + $keys[] = [ + button(getTranslation('back'), formatCallbackData($backData)), + button(getTranslation('abort'), 'exit') + ]; } // Build callback message string. if ($arg != "min" && $arg != "clock") { - $callback_response = getTranslation('pokemon_saved'); + $callback_response = getTranslation('pokemon_saved'); } else { - $callback_response = getTranslation('raid_starts_when_view_changed'); + $callback_response = getTranslation('raid_starts_when_view_changed'); } // Telegram JSON array. @@ -185,12 +156,12 @@ // Set the message. if ($arg == 'min') { - $msg = getTranslation('raid_starts_when_minutes'); + $msg = getTranslation('raid_starts_when_minutes'); } else if ($event_id == EVENT_ID_EX) { - $msg = getTranslation('raid_starts_when'); - $msg .= CR . CR . getTranslation('raid_select_date'); + $msg = getTranslation('raid_starts_when'); + $msg .= CR . CR . getTranslation('raid_select_date'); } else { - $msg = getTranslation('raid_starts_when'); + $msg = getTranslation('raid_starts_when'); } // Edit the message. @@ -198,6 +169,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/edit_time.php b/mods/edit_time.php index a6bd203b..01a5ca98 100644 --- a/mods/edit_time.php +++ b/mods/edit_time.php @@ -1,56 +1,24 @@ accessCheck('create'); -// Count 3 means we received pokemon_table_id and starttime -// Count 4 means we received pokemon_table_id, starttime and an optional argument -// Count 5 means we received pokemon_table_id, starttime, optional argument and slot switch -$arg_data = explode(',', $data['arg']); -$count_arg = count($arg_data); -$event_id = $arg_data[0]; -$raid_level = $arg_data[1]; -$opt_arg = 'new-raid'; -$slot_switch = 0; -if($count_arg >= 4) { - $pokemon_table_id = $arg_data[2]; - $starttime = $arg_data[3]; -} -if($count_arg >= 5) { - $opt_arg = $arg_data[4]; -} -if($count_arg >= 6) { - $slot_switch = $arg_data[5]; -} - -// Write to log. -debug_log('count_id: ' . $count_id); -debug_log('count_arg: ' . $count_arg); -debug_log('opt_arg: ' . $opt_arg); -debug_log('slot_switch: ' . $slot_switch); +$raid_id = $data['r'] ?? 0; +$gym_id = $data['g'] ?? 0; +$event_id = $data['e'] ?? NULL; +$raid_level = $data['rl'] ?? 0; +$pokemon_table_id = $data['p'] ?? 0; +$starttime = $data['t'] ?? 0; +$opt_arg = $data['o'] ?? 'new-raid'; // Telegram JSON array. $tg_json = array(); @@ -59,181 +27,147 @@ // raid_id is 0, means we did not create it yet // gym_id is not 0, means we have a gym_id for creation if ($raid_id == 0 && $gym_id != 0) { - // Replace "-" with ":" to get proper time format - debug_log('Formatting the raid time properly now.'); - $arg_time = str_replace('-', ':', $starttime); - - // Event Raid or normal/EX raid? - if($event_id == 'X') { - debug_log('Ex-Raid time :D ... Setting raid date to ' . $arg_time); - $start_date_time = $arg_time; - $duration = $config->RAID_DURATION; - }elseif($event_id != 'N') { - debug_log('Event time :D ... Setting raid date to ' . $arg_time); - $start_date_time = $arg_time; - $query = my_query("SELECT raid_duration FROM events WHERE id = '{$event_id}' LIMIT 1"); - $result = $query->fetch(); - $duration = $result['raid_duration']; - } else { - // Current date - $current_date = date('Y-m-d', strtotime('now')); - debug_log('Today is a raid day! Setting raid date to ' . $current_date); - // Raid time - $start_date_time = $current_date . ' ' . $arg_time . ':00'; - debug_log('Received the following time for the raid: ' . $start_date_time); - $duration = $config->RAID_DURATION; + // Replace "-" with ":" to get proper time format + debug_log('Formatting the raid time properly now.'); + + // Date was received in format YearMonthDayHourMinute so we need to reformat it to datetime + $start_date_time = substr($starttime,0,4) . '-' . substr($starttime,4,2) . '-' . substr($starttime,6,2) . ' ' . substr($starttime,8,2) . ':' . substr($starttime,10,2) . ':00'; + // Event raids + if($event_id != NULL) { + debug_log('Event time :D ... Setting raid date to ' . $start_date_time); + $query = my_query('SELECT raid_duration FROM events WHERE id = ? LIMIT 1', [$event_id]); + $result = $query->fetch(); + $duration = $result['raid_duration'] == 0 ? $config->RAID_DURATION : $result['raid_duration']; + $egg_duration = $config->RAID_EGG_DURATION; + + // Elite raids + }elseif($raid_level == 9) { + debug_log('Elite Raid time :D ... Setting raid date to ' . $start_date_time); + $duration = $config->RAID_DURATION_ELITE; + $egg_duration = $config->RAID_EGG_DURATION_ELITE; + + // Normal raids + } else { + debug_log('Received the following time for the raid: ' . $start_date_time); + $duration = $config->RAID_DURATION; + $egg_duration = $config->RAID_EGG_DURATION; + } + + // Check for duplicate raid + $duplicate_id = active_raid_duplication_check($gym_id); + + // Tell user the raid already exists and exit! + // Unless we are creating an event raid + if($duplicate_id != 0 && + !($event_id == EVENT_ID_EX && $botUser->accessCheck('ex-raids', true)) && + !($event_id != EVENT_ID_EX && $event_id != NULL && $botUser->accessCheck('event-raids', true)) + ) { + $keys = []; + $raid_id = $duplicate_id; + $raid = get_raid($raid_id); + $msg = EMOJI_WARN . SP . getTranslation('raid_already_exists') . SP . EMOJI_WARN . CR . show_raid_poll_small($raid); + + $keys = share_keys($raid_id, 'raid_share', $update, $raid['level']); + + // Add keys for sharing the raid. + if(!empty($keys)) { + // Exit key + $keys[][] = button(getTranslation('abort'), 'exit'); } - // Check for duplicate raid - $duplicate_id = 0; - if($raid_id == 0) { - $duplicate_id = active_raid_duplication_check($gym_id); - } - - // Continue with raid creation - if($duplicate_id == 0) { - // Now. - $now = utcnow(); - - $pokemon_id_formid = get_pokemon_by_table_id($pokemon_table_id); - - // Saving event info to db. N = null - $event = (($event_id == "N") ? "NULL" : (($event_id=="X") ? EVENT_ID_EX : $event_id )); - debug_log("Event: ".$event); - debug_log("Event-id: ".$event_id); - debug_log("Raid level: ".$raid_level); - debug_log("Pokemon: ".$pokemon_id_formid['pokedex_id']."-".$pokemon_id_formid['pokemon_form_id']); - - // Create raid in database. - $rs = my_query( - " - INSERT INTO raids - SET user_id = {$update['callback_query']['from']['id']}, - pokemon = '{$pokemon_id_formid['pokedex_id']}', - pokemon_form = '{$pokemon_id_formid['pokemon_form_id']}', - start_time = '{$start_date_time}', - spawn = DATE_SUB(start_time, INTERVAL ".$config->RAID_EGG_DURATION." MINUTE), - end_time = DATE_ADD(start_time, INTERVAL {$duration} MINUTE), - gym_id = '{$gym_id}', - level = '{$raid_level}', - event = {$event} - " - ); - - // Get last insert id from db. - $raid_id = $dbh->lastInsertId(); - - // Write to log. - debug_log('ID=' . $raid_id); - - // Tell user the raid already exists and exit! - } else { - $keys = []; - $raid_id = $duplicate_id; - $raid = get_raid($raid_id); - $msg = EMOJI_WARN . SP . getTranslation('raid_already_exists') . SP . EMOJI_WARN . CR . show_raid_poll_small($raid); - - // Check if the raid was already shared. - $rs_share = my_query( - " - SELECT COUNT(*) AS raid_count - FROM cleanup - WHERE raid_id = '{$raid_id}' - " - ); - - $shared = $rs_share->fetch(); - - // Add keys for sharing the raid. - if($shared['raid_count'] == 0) { - $keys = share_keys($raid_id, 'raid_share', $update); - - // Exit key - $empty_exit_key = []; - $key_exit = universal_key($empty_exit_key, '0', 'exit', '0', getTranslation('abort')); - $keys = array_merge($keys, $key_exit); - } - - // Answer callback. - $tg_json[] = answerCallbackQuery($update['callback_query']['id'], getTranslation('raid_already_exists'), true); - - // Edit the message. - $tg_json[] = edit_message($update, $msg, $keys, false, true); - - // Telegram multicurl request. - curl_json_multi_request($tg_json); - - // Exit. - exit(); - } + // Answer callback. + $tg_json[] = answerCallbackQuery($update['callback_query']['id'], getTranslation('raid_already_exists'), true); + + // Edit the message. + $tg_json[] = edit_message($update, $msg, $keys, false, true); + + // Telegram multicurl request. + curl_json_multi_request($tg_json); + + // Exit. + exit(); + } + // Continue with raid creation + $pokemon_id_formid = get_pokemon_by_table_id($pokemon_table_id); + + // Saving event info to db. N = null + debug_log("Event-id: ".$event_id); + debug_log("Raid level: ".$raid_level); + debug_log("Pokemon: ".$pokemon_id_formid['pokedex_id']."-".$pokemon_id_formid['pokemon_form_id']); + + // Create raid in database. + $rs = my_query(' + INSERT INTO raids + SET user_id = :userId, + pokemon = :pokemon, + pokemon_form = :pokemonForm, + start_time = :startTime, + spawn = DATE_SUB(start_time, INTERVAL ' . $egg_duration . ' MINUTE), + end_time = DATE_ADD(start_time, INTERVAL ' . $duration . ' MINUTE), + gym_id = :gymId, + level = :level, + event = :event + ', [ + 'userId' => $update['callback_query']['from']['id'], + 'pokemon' => $pokemon_id_formid['pokedex_id'], + 'pokemonForm' => $pokemon_id_formid['pokemon_form_id'], + 'startTime' => $start_date_time, + 'gymId' => $gym_id, + 'level' => $raid_level, + 'event' => $event_id, + ]); + + // Get last insert id from db. + $raid_id = $dbh->lastInsertId(); + + // Write to log. + debug_log('ID=' . $raid_id); } // Init empty keys array. $keys = []; // Raid pokemon duration short or 1 Minute / 5 minute time slots -if($opt_arg == 'more') { - // 1-minute selection - $slotsize = 1; - - $slotmax = $config->RAID_DURATION; - - for ($i = $slotmax; $i >= 15; $i = $i - $slotsize) { - // Create the keys. - $keys[] = array( - // Just show the time, no text - not everyone has a phone or tablet with a large screen... - 'text' => floor($i / 60) . ':' . str_pad($i % 60, 2, '0', STR_PAD_LEFT), - 'callback_data' => $raid_id . ':edit_save:' . $i - ); - } +if($opt_arg == 'm') { + // 1-minute selection + $slotsize = 1; + + $slotmax = $config->RAID_DURATION; + + for ($i = $slotmax; $i >= 15; $i = $i - $slotsize) { + // Create the keys. + $buttonText = floor($i / 60) . ':' . str_pad($i % 60, 2, '0', STR_PAD_LEFT); + $keys[] = button($buttonText, ['edit_save', 'd' => $i, 'r' => $raid_id]); + } } else { - debug_log('Comparing slot switch and argument for fast forward'); - if ($slot_switch == 0) { - $raidduration = $config->RAID_DURATION; - - // Write to log. - debug_log('Doing a fast forward now!'); - debug_log('Changing data array first...'); - - // Reset data array - $data = []; - $data['id'] = $raid_id; - $data['action'] = 'edit_save'; - $data['arg'] = $raidduration; - - // Write to log. - debug_log($data, '* NEW DATA= '); - - // Set module path by sent action name. - $module = ROOT_PATH . '/mods/edit_save.php'; - - // Write module to log. - debug_log($module); - - // Check if the module file exists. - if (file_exists($module)) { - // Dynamically include module file and exit. - include_once($module); - exit(); - } - } else { - - // Use raid pokemon duration short. - // Use normal raid duration. - $keys[] = array( - 'text' => '0:' . $config->RAID_DURATION, - 'callback_data' => $raid_id . ':edit_save:' . $config->RAID_DURATION - ); - - // Button for more options. - $keys[] = array( - 'text' => getTranslation('expand'), - 'callback_data' => $raid_id . ':edit_time:' . $pokemon_id . ',' . $start_time . ',more,' . $slot_switch - ); - - - } + debug_log('Comparing slot switch and argument for fast forward'); + $raidduration = $config->RAID_DURATION; + + // Write to log. + debug_log('Doing a fast forward now!'); + debug_log('Changing data array first...'); + + // Reset data array + $data = []; + $data['r'] = $raid_id; + $data[0] = 'edit_save'; + + // Write to log. + debug_log($data, '* NEW DATA= '); + + // Set module path by sent action name. + $module = ROOT_PATH . '/mods/edit_save.php'; + + // Write module to log. + debug_log($module); + + // Check if the module file exists. + if (file_exists($module)) { + // Dynamically include module file and exit. + include_once($module); + exit(); + } } // Get the inline key array. @@ -243,10 +177,10 @@ debug_log($keys); // Build callback message string. -if ($opt_arg != 'more' && $event_id == 'N') { - $callback_response = getTranslation('start_date_time') . ' ' . $arg_time; +if ($opt_arg != 'more' && $event_id == NULL) { + $callback_response = 'OK'; } else { - $callback_response = getTranslation('raid_starts_when_view_changed'); + $callback_response = getTranslation('raid_starts_when_view_changed'); } // Answer callback. @@ -257,6 +191,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/end_remote_raid.php b/mods/end_remote_raid.php index 31c78416..bf25577e 100644 --- a/mods/end_remote_raid.php +++ b/mods/end_remote_raid.php @@ -1,14 +1,13 @@ diff --git a/mods/events.php b/mods/events.php new file mode 100644 index 00000000..4da5ef71 --- /dev/null +++ b/mods/events.php @@ -0,0 +1,36 @@ +accessCheck('event-manage'); + +$keys = []; +$callback_response = 'OK'; + +// Manage events +$q = my_query('SELECT * FROM events'); +$msg = '' . getTranslation('events_manage') . '' . CR; +foreach($q->fetchAll() as $event) { + if($event['id'] == EVENT_ID_EX) $event['name'] = getTranslation('Xstars'); + if(empty($event['description'])) $event['description'] = '' . getTranslation('events_no_description') . ''; + $msg .= '' . $event['name'] . '' . CR; + $msg .= $event['description'] . CR . CR; + $keys[][] = button($event['name'], ['events_manage', 'e' => $event['id']]); +} +$keys[][] = button(getTranslation('done'), ['exit', 'd' => '1']); + +$tg_json = []; + +// Answer callback. +$tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_response, true); + +// Edit the message. +$tg_json[] = edit_message($update, $msg, $keys, false, true); + +// Telegram multicurl request. +curl_json_multi_request($tg_json); diff --git a/mods/events_add.php b/mods/events_add.php new file mode 100644 index 00000000..c10d61e6 --- /dev/null +++ b/mods/events_add.php @@ -0,0 +1,60 @@ +accessCheck('event-manage'); + +$abort = $data['a'] ?? 0; + +$keys = []; +$callback_response = 'OK'; +$userId = $update['callback_query']['from']['id'] ?? $update['message']['from']['id']; + +if(isset($modifiers)) { + $value = htmlspecialchars(trim($update['message']['text'])); + my_query('INSERT INTO events SET name=?, description=""',[$value]); + $eventId = $dbh->lastInsertId(); + $callback_response = getTranslation('done'); + editMessageText($modifiers['old_message_id'], getTranslation('events_created'), [], $userId); + $msg = '' . getTranslation('events_created') . '' . CR; + $msg .= $value; + $keys[][] = button(getTranslation('next'), ['events_manage', 'e' => $eventId]); +}else { + if($abort == 0) { + // Add a new event + $msg = '' . getTranslation('events_create') . '' . CR; + $msg .= getTranslation('events_give_name') . ':'; + + $modifiers = json_encode(['old_message_id'=>$update['callback_query']['message']['message_id']]); + $userId = $update['callback_query']['from']['id']; + + // Data for handling response from the user + my_query('INSERT INTO user_input SET user_id = ?, handler = \'events_add\', modifiers = ?', [$userId, $modifiers]); + + $keys[][] = button(getTranslation('abort'), ['events_add', 'a' => 1]); + }elseif($abort == 1) { + my_query('DELETE FROM user_input WHERE user_id = ?', [$userId]); + answerCallbackQuery($update['callback_query']['id'], 'OK'); + editMessageText($update['callback_query']['message']['message_id'], getTranslation('action_aborted'), [], $userId); + exit; + } +} + +$tg_json = []; + +if(isset($update['callback_query'])) { + // Answer callback. + $tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_response, true); + // Edit the message. + $tg_json[] = edit_message($update, $msg, $keys, false, true); +}else { + $tg_json[] = send_message(create_chat_object([$update['message']['chat']['id']]), $msg, $keys, false, true); +} + +// Telegram multicurl request. +curl_json_multi_request($tg_json); diff --git a/mods/events_manage.php b/mods/events_manage.php new file mode 100644 index 00000000..97cb6906 --- /dev/null +++ b/mods/events_manage.php @@ -0,0 +1,194 @@ +accessCheck('event-manage'); + +$columnSettings = [ + 'vote_key_mode' => ['allowed' => [0,1], 'default' => 0, 'nullable' => false], + 'pokemon_title' => ['allowed' => [0,1,2], 'default' => 1, 'nullable' => false], + 'hide_raid_picture' => ['allowed' => [0,1], 'default' => 0, 'nullable' => false], + 'time_slots' => ['nullable' => true], + 'raid_duration' => ['nullable' => false], + 'poll_template' => ['nullable' => true], +]; + +$eventId = $data['e'] ?? false; +$action = $data['a'] ?? false; +$keys = []; +$callback_response = 'OK'; +$userId = $update['callback_query']['from']['id'] ?? $update['message']['from']['id']; + +// Process user input +if(isset($modifiers) && isset($modifiers['action'])) { + $value = htmlspecialchars(trim($update['message']['text'])); + if($modifiers['action'] == 1) { + // User input is new event name + $column = 'name'; + $action = 0; + }else if($modifiers['action'] == 2) { + // User input is new description + $column = 'description'; + $action = 0; + }else if($modifiers['action'] == 3) { + // User input is raid poll settings + $column = $modifiers['column']; + // Validate input + if($columnSettings[$column]['nullable'] && strtolower($value) == 'null') { + $value = NULL; + } + if(in_array($column, ['vote_key_mode','time_slots','raid_duration','hide_raid_picture','pokemon_title']) && $value != NULL) { + $value = preg_replace('/\D/', '', $value); + if(isset($columnSettings[$column]['allowed']) && !in_array($value, $columnSettings[$column]['allowed'])) $value = $columnSettings[$column]['default']; + }elseif($column == 'poll_template' && $value != NULL) { + $rows = preg_split("/\r\n|\n|\r/", $value); + $inputArray = []; + $i = 0; + // Convert input into json array + foreach($rows as $row) { + $buttons = explode(',', $row); + foreach($buttons as $button) { + $button = trim($button); + if(in_array($button, ['alone', 'extra', 'extra_alien', 'remote', 'inv_plz', 'can_inv', 'ex_inv', 'teamlvl', 'time', 'pokemon', 'refresh', 'alarm', 'here', 'late', 'done', 'cancel'])) { + $inputArray[$i][] = $button; + } + } + $i++; + } + $value = (strtolower($value) == 'null' ? NULL : json_encode($inputArray)); + } + $action = 3; + } + $eventId = $modifiers['eventId']; + my_query('UPDATE events SET ' . $column . ' = ? WHERE id=?', [$value, $eventId]); + $callback_response = getTranslation('done'); + editMessageText($modifiers['old_message_id'], getTranslation('updated'), [], $userId); +} + +$q = my_query('SELECT * FROM events where id = ?', [$eventId]); +$event = $q->fetch(); +if($eventId == EVENT_ID_EX) { + $event['name'] = getTranslation('Xstars'); +} +if(empty($event['description'])) $event['description'] = '' . getTranslation('events_no_description') . ''; + +$msg = '' . getTranslation('events_manage') . '' . CR . CR; +$msg .= '' . $event['name'] . '' . CR; +$msg .= $event['description'] . CR . CR; + +if($action == 0 || $action == 'a') { + if($action == 'a') { + my_query('DELETE FROM user_input WHERE user_id=?', [$userId]); + $callback_response = getTranslation('action_aborted'); + } + if($eventId != EVENT_ID_EX) + $keys[][] = button(getTranslation('events_edit_name'), ['events_manage', 'e' => $eventId, 'a' => 1]); + $keys[][] = button(getTranslation('events_edit_description'), ['events_manage', 'e' => $eventId, 'a' => 2]); + $keys[][] = button(getTranslation('events_edit_raid_poll'), ['events_manage', 'e' => $eventId, 'a' => 3]); + if($eventId != EVENT_ID_EX) + $keys[][] = button(getTranslation('events_delete'), ['events_manage', 'e' => $eventId, 'a' => 4]); + + $keys[] = [ + button(getTranslation('back'), 'events'), + button(getTranslation('done'), 'exit') + ]; + +// Edit event name +}else if($action == 1) { + $modifiers = json_encode(['old_message_id'=>$update['callback_query']['message']['message_id'],'action'=>1,'eventId'=>$eventId]); + my_query('INSERT INTO user_input SET user_id=?, handler=\'events_manage\', modifiers=?', [$userId, $modifiers]); + + $msg .= '' . getTranslation('events_edit_name') . '' . CR; + $msg .= getTranslation('events_give_name') . ':' . CR; + $keys[][] = button(getTranslation('abort'), ['events_manage', 'e' => $eventId, 'a' => 'a']); + +// Edit event description +}else if($action == 2) { + $modifiers = json_encode(['old_message_id'=>$update['callback_query']['message']['message_id'],'action'=>2,'eventId'=>$eventId]); + my_query('INSERT INTO user_input SET user_id=?, handler=\'events_manage\', modifiers=?', [$userId, $modifiers]); + $msg .= '' . getTranslation('events_edit_description') . '' . CR; + $msg .= getTranslation('events_give_description') . ':'; + $keys[][] = button(getTranslation('abort'), ['events_manage', 'e' => $eventId, 'a' => 'a']); + +// Edt event raid poll settings +}else if($action == 3) { + my_query('DELETE FROM user_input WHERE user_id=?', [$userId]); + $templateArray = ($event['poll_template'] == NULL) ? $config->RAID_POLL_UI_TEMPLATE : json_decode($event['poll_template'], true); + $event['poll_template'] = templateJsonToString($templateArray); + $printColumns = ['vote_key_mode','time_slots','raid_duration','hide_raid_picture','pokemon_title','poll_template']; + + $msg .= 'https://pokemonraidbot.readthedocs.io/en/latest/config.html#event-raids' . CR; + $msg .= 'https://pokemonraidbot.readthedocs.io/en/latest/config.html#raid-poll-design-and-layout' . CR . CR; + foreach($printColumns as $column) { + $msg .= $column . ': '; + $msg .= ($column == 'poll_template' ? CR : ''); + $msg .= '' . ($event[$column] === NULL ? 'NULL' : $event[$column]) . '' . CR; + $keys[][] = button($column, ['events_manage', 'e' => $eventId, 'a' => 'e', 'c' => $column]); + } + $keys[] = [ + button(getTranslation('back'), ['events_manage', 'e' => $eventId, 'a' => 0]), + button(getTranslation('done'), ['exit', 'd' => 1]), + ]; + +// Delete event confirmation +}else if($action == 4) { + $msg .= '' . getTranslation('events_delete_confirmation') . '' . CR; + $keys[] = [ + button(getTranslation('yes'), ['events_manage', 'e' => $eventId, 'a' => 'd']), + button(getTranslation('no'), ['events_manage', 'e' => $eventId, 'a' => 0]), + ]; + +// Delete event +}else if($action == 'd') { + if($eventId != EVENT_ID_EX) my_query('DELETE FROM events WHERE id=?', [$eventId]); + include(ROOT_PATH . '/mods/events.php'); + exit; + +// Prompt for raid poll value editing +}else if($action == 'e') { + $valueToEdit = $data['c']; + $modifiers = json_encode(['old_message_id'=>$update['callback_query']['message']['message_id'],'action'=>3,'column'=>$valueToEdit,'eventId'=>$eventId]); + my_query('INSERT INTO user_input SET user_id=?, handler=\'events_manage\', modifiers=?', [$userId, $modifiers]); + + if($valueToEdit == 'poll_template') { + $templateArray = ($event['poll_template'] == NULL) ? $config->RAID_POLL_UI_TEMPLATE : json_decode($event['poll_template'], true); + $event['poll_template'] = templateJsonToString($templateArray); + } + + $msg .= $valueToEdit . CR; + $msg .= getTranslation('old_value') . CR; + $msg .= '' . ($event[$valueToEdit] === NULL ? 'NULL' : $event[$valueToEdit]) . '' . CR . CR; + $msg .= getTranslation('new_value'); + $keys[][] = button(getTranslation('back'), ['events_manage', 'e' => $eventId, 'a' => 3]); + +} + +$tg_json = []; + +if(isset($update['callback_query'])) { + // Answer callback. + $tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_response, true); + // Edit the message. + $tg_json[] = edit_message($update, $msg, $keys, false, true); +}else { + $tg_json[] = send_message(create_chat_object([$update['message']['chat']['id']]), $msg, $keys, false, true); +} + +// Telegram multicurl request. +curl_json_multi_request($tg_json); + +function templateJsonToString($templateArray) { + $templateString = ''; + foreach($templateArray as $line) { + foreach($line as $button) { + $templateString .= $button . ','; + } + $templateString = rtrim($templateString, ',') . CR; + } + return $templateString; +} diff --git a/mods/exit.php b/mods/exit.php index c1d5dee2..953e057a 100644 --- a/mods/exit.php +++ b/mods/exit.php @@ -8,9 +8,10 @@ // Set empty keys. $keys = []; +$arg = $data['d'] ?? 0; // Build message string. -$msg = ($data['arg'] == 1) ? (getTranslation('done') . '!') : (getTranslation('action_aborted')); +$msg = ($arg == 1) ? (getTranslation('done') . '!') : (getTranslation('action_aborted')); // Telegram JSON array. $tg_json = array(); @@ -23,6 +24,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/getdb.php b/mods/getdb.php index f2d80b19..03309505 100644 --- a/mods/getdb.php +++ b/mods/getdb.php @@ -1,388 +1,213 @@ $pokemon_name, - 'pokemon_form_name'=>$form_name, - 'pokemon_form_id'=>0, - 'asset_suffix'=>0, - 'shiny'=>0, - 'min_cp'=>0, - 'max_cp'=>0, - 'min_weather_cp'=>0, - 'max_weather_cp'=>0, - 'type' => '', - 'type2' => '', - 'shiny'=>0, - 'weather'=>0 - ]; - } - $i = 0; - $SQL = ''; - foreach($pokemon_array as $id => $forms) { - $pokemon_id = $id; - foreach($forms as $form=>$data) { - // Check that data is set, if not the mon is probably not in the game yet and there's no point in having them in a broken state - if(isset($data['weather']) && isset($data['min_cp']) && isset($data['max_cp']) && isset($data['min_weather_cp']) && isset($data['max_weather_cp'])) { - $poke_form = $form; - - $poke_name = $data['pokemon_name']; - $form_id = $data['pokemon_form_id']; - $form_asset_suffix = $data['asset_suffix']; - $poke_min_cp = $data['min_cp']; - $poke_max_cp = $data['max_cp']; - $poke_min_weather_cp = $data['min_weather_cp']; - $poke_max_weather_cp = $data['max_weather_cp']; - $poke_type = $data['type']; - $poke_type2 = $data['type2']; - $poke_weather = $data['weather']; - - if($pokemon_id == 150 && $data['pokemon_form_name']=="a") { - // Because logic and consistency - $poke_form = 'armored'; - }else { - $poke_form = strtolower($data['pokemon_form_name']); - } - if($i==0) $i=1; else $SQL .= ","; - $SQL .= PHP_EOL . "(\"${pokemon_id}\", \"${poke_name}\", \"${poke_form}\", \"${form_id}\", \"${form_asset_suffix}\", \"${poke_min_cp}\", \"${poke_max_cp}\", \"${poke_min_weather_cp}\", \"${poke_max_weather_cp}\", \"${poke_type}\", \"${poke_type2}\", \"${poke_weather}\")"; - } - } - } - ## MySQL 8 compatible - #$SQL = $PRE . $SQL . ' as new' . PHP_EOL; - #$SQL .= 'ON DUPLICATE KEY UPDATE pokedex_id = new.pokedex_id, pokemon_name = new.pokemon_name, pokemon_form_name = new.pokemon_form_name,' . PHP_EOL; - #$SQL .= 'pokemon_form_id = new.pokemon_form_id, asset_suffix = new.asset_suffix, min_cp = new.min_cp, max_cp = new.max_cp,' . PHP_EOL; - #$SQL .= 'min_weather_cp = new.min_weather_cp, max_weather_cp = new.max_weather_cp, type = new.type, type2 = new.type2, weather = new.weather;'; - $SQL = $PRE . $SQL . PHP_EOL; - $SQL .= 'ON DUPLICATE KEY UPDATE pokedex_id = VALUES(pokedex_id), pokemon_name = VALUES(pokemon_name), pokemon_form_name = VALUES(pokemon_form_name),' . PHP_EOL; - $SQL .= 'pokemon_form_id = VALUES(pokemon_form_id), asset_suffix = VALUES(asset_suffix), min_cp = VALUES(min_cp),' . PHP_EOL; - $SQL .= 'max_cp = VALUES(max_cp), min_weather_cp = VALUES(min_weather_cp), max_weather_cp = VALUES(max_weather_cp),' . PHP_EOL; - $SQL .= 'type = VALUES(type), type2 = VALUES(type2), weather = VALUES(weather);' . PHP_EOL; - try { - $prep = $dbh->prepare($SQL); - $prep->execute(); - } catch (Exception $e) { - if(isset($update['message']['from']['id'])) $error = $e; - } - } - }else { - $error = 'Failed to write costume data to protos/costume.json'; - } -} else { - $error = 'Failed to get protos.'; +# We only have an update if the call came from TG +if(!isset($update)){ + $update = false; } -if(!$error) { - $msg = 'Updated successfully!' . CR; - $msg.= $prep->rowCount() . ' rows required updating!'; - // Sometimes Nia can push form id's a bit later than other stats, so the script may insert incomplete rows - // This hopefully clears those faulty rows out when the complete data is received without effecting any actual data - my_query(' - DELETE t1 FROM pokemon t1 - INNER JOIN pokemon t2 - WHERE - t1.pokedex_id = t2.pokedex_id - AND t1.pokemon_form_name = t2.pokemon_form_name - AND t1.pokemon_form_name <> \'normal\' - AND t1.pokemon_form_id = 0 - '); - $callback_msg = 'OK!'; -}else { - $msg = $error; - info_log('Pokemon table update failed: ' . $error); - $callback_msg = 'Error!'; + +// Parse the game master data together with form ids into format we can use +[$pokemon_array, $costume_data] = parse_master_data($game_master_url); +if(!$pokemon_array) { + sendResults('Failed to open game master file.', $update, true); } -if(isset($update['callback_query']['id'])) { - // Answer callback. - $tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_msg, true); - // Edit the message. - $tg_json[] = editMessageText($update['callback_query']['message']['message_id'], $msg, [], $update['callback_query']['message']['chat']['id'], false, true); +// Save our core datasets to json files for further use +if(!file_put_contents(ROOT_PATH.'/protos/costume.json', json_encode($costume_data, JSON_PRETTY_PRINT))) { + sendResults('Failed to write costume data to protos/costume.json', $update, true); +} - // Telegram multicurl request. - curl_json_multi_request($tg_json); +// Craft egg data +$PRE = 'INSERT INTO `pokemon`' . PHP_EOL; +$PRE .= '(pokedex_id, pokemon_name, pokemon_form_name, pokemon_form_id, min_cp, max_cp, min_weather_cp, max_weather_cp, type, type2, weather) VALUES'; +foreach(EGGS as $egg) { + $pokemon_id = $egg; + $pokemon_name = 'Level '. str_replace('999', '', $egg) .' Egg'; + $pokemon_array[$pokemon_id]['normal'] = [ + 'pokemon_name' => $pokemon_name, + 'pokemon_form_name' => 'normal', + 'pokemon_form_id' => 0, + 'shiny' => 0, + 'min_cp' => 0, + 'max_cp' => 0, + 'min_weather_cp' => 0, + 'max_weather_cp' => 0, + 'type' => '', + 'type2' => '', + 'weather' => 0, + ]; +} - // Exit. - $dbh = null; +// Craft the rest of the pokemon data +$i = 0; +$dataSql = ''; +foreach($pokemon_array as $pokemon_id => $forms) { + foreach($forms as $form => $data) { + // Check that data is set, if not the mon is probably not in the game yet and there's no point in having them in a broken state + if(!isset($data['weather']) || !isset($data['min_cp']) || !isset($data['max_cp']) || !isset($data['min_weather_cp']) || !isset($data['max_weather_cp']) || !isset($data['pokemon_name'])) continue; + $insertData = [$pokemon_id, $data['pokemon_name'], $data['pokemon_form_name'], $data['pokemon_form_id'], $data['min_cp'], $data['max_cp'], $data['min_weather_cp'], $data['max_weather_cp'], $data['type'], $data['type2'], $data['weather']]; + $dataSql .= PHP_EOL . '("' . implode('","', $insertData) . '"),'; + } +} +## MySQL 8 compatible +#$SQL = $PRE . $SQL . ' as new' . PHP_EOL; +#$SQL .= 'ON DUPLICATE KEY UPDATE pokedex_id = new.pokedex_id, pokemon_name = new.pokemon_name, pokemon_form_name = new.pokemon_form_name,' . PHP_EOL; +#$SQL .= 'pokemon_form_id = new.pokemon_form_id, min_cp = new.min_cp, max_cp = new.max_cp,' . PHP_EOL; +#$SQL .= 'min_weather_cp = new.min_weather_cp, max_weather_cp = new.max_weather_cp, type = new.type, type2 = new.type2, weather = new.weather;'; +$SQL = $PRE . rtrim($dataSql, ',') . PHP_EOL; +$SQL .= 'ON DUPLICATE KEY UPDATE pokedex_id = VALUES(pokedex_id), pokemon_name = VALUES(pokemon_name), pokemon_form_name = VALUES(pokemon_form_name),' . PHP_EOL; +$SQL .= 'pokemon_form_id = VALUES(pokemon_form_id), min_cp = VALUES(min_cp),' . PHP_EOL; +$SQL .= 'max_cp = VALUES(max_cp), min_weather_cp = VALUES(min_weather_cp), max_weather_cp = VALUES(max_weather_cp),' . PHP_EOL; +$SQL .= 'type = VALUES(type), type2 = VALUES(type2), weather = VALUES(weather);' . PHP_EOL; +try { + $prep = $dbh->prepare($SQL); + $prep->execute(); +} catch (Exception $e) { + sendResults($e, $update, true); +} +$msg = 'Updated successfully!' . CR; +$msg.= $prep->rowCount() . ' rows required updating!'; +// Sometimes Nia can push form id's a bit later than other stats, so the script may insert incomplete rows +// This hopefully clears those faulty rows out when the complete data is received without effecting any actual data +my_query(' + DELETE t1 FROM pokemon t1 + INNER JOIN pokemon t2 + WHERE + t1.pokedex_id = t2.pokedex_id + AND t1.pokemon_form_name = t2.pokemon_form_name + AND t1.pokemon_form_name <> \'normal\' + AND t1.pokemon_form_id = 0 +'); +sendResults($msg, $update); + +function sendResults($msg, $update, $error = false) { + if($error) { + info_log('Pokemon table update failed: ' . $msg); + }else if(!isset($update['callback_query']['id'])) { + info_log($msg); exit(); + } + $tg_json[] = answerCallbackQuery($update['callback_query']['id'], (!$error) ? 'OK!' : 'Error!', true); + $tg_json[] = editMessageText($update['callback_query']['message']['message_id'], $msg, [], $update['callback_query']['message']['chat']['id'], false, true); + curl_json_multi_request($tg_json); + exit; } - function calculate_cps($base_stats) { - // CP = (Attack * Defense^0.5 * Stamina^0.5 * CP_Multiplier^2) / 10 - $cp_multiplier = array(20 => 0.5974 ,25 =>0.667934 ); - $min = floor((($base_stats['baseAttack']+10)*(($base_stats['baseDefense']+10)**0.5)*(($base_stats['baseStamina']+10)**0.5)*$cp_multiplier[20]**2)/10); - $max = floor((($base_stats['baseAttack']+15)*(($base_stats['baseDefense']+15)**0.5)*(($base_stats['baseStamina']+15)**0.5)*$cp_multiplier[20]**2)/10); - $min_weather = floor((($base_stats['baseAttack']+10)*(($base_stats['baseDefense']+10)**0.5)*(($base_stats['baseStamina']+10)**0.5)*$cp_multiplier[25]**2)/10); - $max_weather = floor((($base_stats['baseAttack']+15)*(($base_stats['baseDefense']+15)**0.5)*(($base_stats['baseStamina']+15)**0.5)*$cp_multiplier[25]**2)/10); - return [$min,$max,$min_weather,$max_weather]; -} - -function get_protos($proto_url) { - //Parse the form ID's from pogoprotos - if(!$proto_file = curl_get_contents($proto_url)) return false; - $proto = preg_split('/\r\n|\r|\n/', $proto_file); - $count = count($proto); - $form_ids = $costume = array(); - $data_array = false; - for($i=0;$i<$count;$i++) { - $line = trim($proto[$i]); - if($data_array != false) { - $data = explode('=',str_replace(';','',$line)); - // End of pokemon data, no need to loop further - if(trim($data[0]) == '}') { - $data_array = false; - if(count($form_ids) > 0 && count($costume) > 0) { - // We found what we needed so we can stop looping through proto file and exit - break; - } - }else if(count($data) == 2) { - ${$data_array}[trim($data[0])] = trim($data[1]); - } - }else { - if($line == 'enum Costume {') { - $data_array = 'costume'; - } - if($line == 'enum Form {') { - $data_array = 'form_ids'; - } - } - } - unset($proto); - return [$form_ids, $costume]; + // CP = (Attack * Defense^0.5 * Stamina^0.5 * CP_Multiplier^2) / 10 + $cp_multiplier = array(20 => 0.5974, 25 => 0.667934); + $min = floor((($base_stats['attack']+10)*(($base_stats['defense']+10)**0.5)*(($base_stats['stamina']+10)**0.5)*$cp_multiplier[20]**2)/10); + $max = floor((($base_stats['attack']+15)*(($base_stats['defense']+15)**0.5)*(($base_stats['stamina']+15)**0.5)*$cp_multiplier[20]**2)/10); + $min_weather = floor((($base_stats['attack']+10)*(($base_stats['defense']+10)**0.5)*(($base_stats['stamina']+10)**0.5)*$cp_multiplier[25]**2)/10); + $max_weather = floor((($base_stats['attack']+15)*(($base_stats['defense']+15)**0.5)*(($base_stats['stamina']+15)**0.5)*$cp_multiplier[25]**2)/10); + return [$min,$max,$min_weather,$max_weather]; } -function parse_master_into_pokemon_table($form_ids, $game_master_url) { - // Set ID's for mega evolutions - // Using negative to prevent mixup with actual form ID's - // Collected from pogoprotos (hoping they won't change, so hard coding them here) - $mega_ids = array('MEGA'=>-1,'MEGA_X'=>-2,'MEGA_Y'=>-3); - - $weatherboost_table = array( - 'POKEMON_TYPE_BUG' => '3', - 'POKEMON_TYPE_DARK' => '8', - 'POKEMON_TYPE_DRAGON' => '6', - 'POKEMON_TYPE_ELECTRIC' => '3', - 'POKEMON_TYPE_FAIRY' => '5', - 'POKEMON_TYPE_FIGHTING' => '5', - 'POKEMON_TYPE_FIRE' => '12', - 'POKEMON_TYPE_FLYING' => '6', - 'POKEMON_TYPE_GHOST' => '8', - 'POKEMON_TYPE_GRASS' => '12', - 'POKEMON_TYPE_GROUND' => '12', - 'POKEMON_TYPE_ICE' => '7', - 'POKEMON_TYPE_NORMAL' => '4', - 'POKEMON_TYPE_POISON' => '5', - 'POKEMON_TYPE_PSYCHIC' => '6', - 'POKEMON_TYPE_ROCK' => '4', - 'POKEMON_TYPE_STEEL' => '7', - 'POKEMON_TYPE_WATER' => '3' - ); - if(!$master_file = curl_get_contents($game_master_url)) return false; - $master = json_decode($master_file, true); - foreach($master as $row) { - $part = explode('_',$row['templateId']); - $form_data = []; - $pokemon_id = ''; - if(count($part)<2) continue; - if($part[0] == 'FORMS' && $part[2] == 'POKEMON') { - // Found Pokemon form data - - // Get pokemon ID - $pokemon_id = ltrim(str_replace('V','',$part[1]),'0'); - unset($part[0]); - unset($part[1]); - unset($part[2]); - - // Pokemon name - $pokemon_name = implode('_',$part); - // Get pokemon forms - if(!isset($row['data']['formSettings']['forms']) or empty($row['data']['formSettings']['forms'][0])) { - $form_data[] = array('form'=>$pokemon_name.'_NORMAL'); - }else { - $form_data = $row['data']['formSettings']['forms']; - } - foreach($form_data as $form) { - $form_name = strtolower(str_replace($pokemon_name.'_','',$form['form'])); - if($form_name != 'purified' && $form_name != 'shadow') { - - // Nidoran - $poke_name = ucfirst(strtolower(str_replace(['_FEMALE','_MALE'],['♀','♂'],$row['data']['formSettings']['pokemon']))); - // Ho-oh - $poke_name = str_replace('_','-',$poke_name); - - if(!isset($form_ids[$form['form']])) { - $form_id = 0; - }else { - $form_id = $form_ids[$form['form']]; - } - $form_asset_suffix = (isset($form['assetBundleValue']) ? $form['assetBundleValue'] : (isset($form['assetBundleSuffix'])?$form['assetBundleSuffix']:'00')); - - $pokemon_array[$pokemon_id][$form_name] = [ 'pokemon_name'=>$poke_name, - 'pokemon_form_name'=>$form_name, - 'pokemon_form_id'=>$form_id, - 'asset_suffix'=>$form_asset_suffix - ]; - - } - } - }else if($part[0] == 'TEMPORARY' && $part[1] == 'EVOLUTION') { - // Found Mega pokemon data - // Get pokemon ID - $pokemon_id = ltrim(str_replace('V','',$part[2]),'0'); - unset($part[0]); - unset($part[1]); - unset($part[2]); - unset($part[3]); - - // Pokemon name - $pokemon_name = implode("_",$part); - $form_data = $row['data']['temporaryEvolutionSettings']['temporaryEvolutions']; - foreach($form_data as $form) { - // Nidoran - $poke_name = ucfirst(strtolower(str_replace(["_FEMALE","_MALE"],["♀","♂"],$pokemon_name))); - // Ho-oh - $poke_name = str_replace("_","-",$poke_name); - - $form_name = str_replace("TEMP_EVOLUTION_","",$form['temporaryEvolutionId']); - $form_asset_suffix = $form['assetBundleValue']; - $form_id = $mega_ids[$form_name]; - - $pokemon_array[$pokemon_id][$form_name] = [ "pokemon_name"=>$poke_name, - "pokemon_form_name"=>$form_name, - "pokemon_form_id"=>$form_id, - "asset_suffix"=>$form_asset_suffix - ]; - } - }else if ($part[1] == "POKEMON" && $part[0][0] == "V" && isset($row['data']['pokemonSettings'])) { - // Found Pokemon data - $pokemon_id = (int)str_replace("V","",$part[0]); - $form_name = str_replace($row['data']['pokemonSettings']['pokemonId']."_","",substr($row['data']['templateId'],14)); - if($form_name != 'PURIFIED' && $form_name != 'SHADOW' && $form_name != 'NORMAL' - && isset($pokemon_array[$pokemon_id]) - && isset($row['data']['pokemonSettings']['stats']['baseAttack']) - && isset($row['data']['pokemonSettings']['stats']['baseDefense']) - && isset($row['data']['pokemonSettings']['stats']['baseStamina'])) { - if($form_name == $row['data']['pokemonSettings']['pokemonId']) { - $form_name = "normal"; - }else { - $form_name = strtolower($form_name); - } - $CPs = calculate_cps($row['data']['pokemonSettings']['stats']); - $min_cp = $CPs[0]; - $max_cp = $CPs[1]; - $min_weather_cp = $CPs[2]; - $max_weather_cp = $CPs[3]; - - $type = strtolower(str_replace('POKEMON_TYPE_','', $row['data']['pokemonSettings']['type'])); - $type2 = ''; - - $weather = $weatherboost_table[$row['data']['pokemonSettings']['type']]; - if(isset($row['data']['pokemonSettings']['type2'])) { - $type2 = strtolower(str_replace('POKEMON_TYPE_','', $row['data']['pokemonSettings']['type2'])); - - # Add type2 weather boost only if there is a second type and it's not the same weather as the first type! - if($weatherboost_table[$row['data']['pokemonSettings']['type2']] != $weatherboost_table[$row['data']['pokemonSettings']['type']]) { - $weather .= $weatherboost_table[$row['data']['pokemonSettings']['type2']]; - } - } - if(isset($pokemon_array[$pokemon_id][$form_name])) { - $pokemon_array[$pokemon_id][$form_name]['min_cp'] = $min_cp; - $pokemon_array[$pokemon_id][$form_name]['max_cp'] = $max_cp; - $pokemon_array[$pokemon_id][$form_name]['min_weather_cp'] = $min_weather_cp; - $pokemon_array[$pokemon_id][$form_name]['max_weather_cp'] = $max_weather_cp; - $pokemon_array[$pokemon_id][$form_name]['weather'] = $weather; - $pokemon_array[$pokemon_id][$form_name]['type'] = $type; - $pokemon_array[$pokemon_id][$form_name]['type2'] = $type2; - }else { - // Fill data for Pokemon that have form data but no stats for forms specifically - foreach($pokemon_array[$pokemon_id] as $form=>$data) { - $pokemon_array[$pokemon_id][$form]['min_cp'] = $min_cp; - $pokemon_array[$pokemon_id][$form]['max_cp'] = $max_cp; - $pokemon_array[$pokemon_id][$form]['min_weather_cp'] = $min_weather_cp; - $pokemon_array[$pokemon_id][$form]['max_weather_cp'] = $max_weather_cp; - $pokemon_array[$pokemon_id][$form]['weather'] = $weather; - $pokemon_array[$pokemon_id][$form]['type'] = $type; - $pokemon_array[$pokemon_id][$form]['type2'] = $type2; - } - } - if(isset($row['data']['pokemonSettings']['tempEvoOverrides'])) { - foreach($row['data']['pokemonSettings']['tempEvoOverrides'] as $temp_evolution) { - if(isset($temp_evolution['tempEvoId'])) { - $form_name = str_replace('TEMP_EVOLUTION_','',$temp_evolution['tempEvoId']); - // We only override the types for megas - // weather info is used to display boosts for caught mons, which often are different from mega's typing - $typeOverride = strtolower(str_replace('POKEMON_TYPE_','', $temp_evolution['typeOverride1'])); - $typeOverride2 = ''; - - if(isset($temp_evolution['typeOverride2'])) { - $typeOverride2 = strtolower(str_replace('POKEMON_TYPE_','', $temp_evolution['typeOverride2'])); - } - $pokemon_array[$pokemon_id][$form_name]['min_cp'] = $min_cp; - $pokemon_array[$pokemon_id][$form_name]['max_cp'] = $max_cp; - $pokemon_array[$pokemon_id][$form_name]['min_weather_cp'] = $min_weather_cp; - $pokemon_array[$pokemon_id][$form_name]['max_weather_cp'] = $max_weather_cp; - $pokemon_array[$pokemon_id][$form_name]['weather'] = $weather; - $pokemon_array[$pokemon_id][$form_name]['type'] = $typeOverride; - $pokemon_array[$pokemon_id][$form_name]['type2'] = $typeOverride2; - } - } - } - } - } +function parse_master_data($game_master_url) { + // Set ID's for mega evolutions + // Using negative to prevent mixup with actual form ID's + // Collected from pogoprotos (hoping they won't change, so hard coding them here) + $mega_names = array(-1 => 'mega', -2 => 'mega_x', -3 => 'mega_y', -4 => 'primal'); + $pokemon_array = []; + $weatherboost_table = array( + 1 => '4', + 2 => '5', + 3 => '6', + 4 => '5', + 5 => '12', + 6 => '4', + 7 => '3', + 8 => '8', + 9 => '7', + 10 => '12', + 11 => '3', + 12 => '12', + 13 => '3', + 14 => '6', + 15 => '7', + 16 => '6', + 17 => '8', + 18 => '5', + ); + if(!$master_file = curl_get_contents($game_master_url)) return false; + $master = json_decode($master_file, true); + foreach($master['pokemon'] as $row) { + $pokemon_id = $row['pokedexId']; + $pokemon_name = $row['name']; + if(!isset($row['stats']['attack']) || !isset($row['stats']['defense']) || !isset($row['stats']['stamina'])) { + continue; } - return $pokemon_array; -} - -// Fetch the latest version of proto files. -// vbase.proto has only the latest fully deobfuscated protos, -// but we only need the latest form and costume data which is available in the partially obfuscated protofiles -function getProtoURL() { - $repo_owner = 'Furtif'; - $repo_name = 'POGOProtos'; - $content_dir = 'base'; - - $repo_content = 'https://api.github.com/repos/' . $repo_owner . '/' . $repo_name . '/contents/' . $content_dir; - // Git tree lookup - $tree = curl_get_contents($repo_content); - $leaf = json_decode($tree, true); - // Detect rate-limiting and die gracefully - if(is_array($leaf) && in_array('message', $leaf)) { - die('Failed to download repo index: ' . $leaf['message']); + $pokemon_types = array_keys($row['types']); + $weather = $weatherboost_table[$pokemon_types[0]]; + if(!isset($pokemon_types[1])) + $pokemon_types[1] = ''; + elseif($weatherboost_table[$pokemon_types[0]] != $weatherboost_table[$pokemon_types[1]]) + $weather .= $weatherboost_table[$pokemon_types[1]]; + foreach($row['forms'] as $formData) { + if($formData['name'] == 'Shadow' || $formData['name'] == 'Purified') continue; + if($formData['name'] == 'Normal') { + $pokemon_array[$pokemon_id]['protoName'] = str_replace('_NORMAL', '', $formData['proto']); + $form_name = 'normal'; + }else { + if(isset($pokemon_array[$pokemon_id]['protoName'])) + $form_name = str_replace($pokemon_array[$pokemon_id]['protoName'].'_', '', $formData['proto']); + else + $form_name = ($formData['proto'] == 'FORM_UNSET') ? 'normal' : $formData['proto']; + } + [$min_cp, $max_cp, $min_weather_cp, $max_weather_cp] = (isset($formData['stats'])) ? calculate_cps($formData['stats']) : calculate_cps($row['stats']); + $form_id = $formData['form']; + $pokemon_array[$pokemon_id][$form_name] = [ + 'pokemon_name' => $pokemon_name, + 'pokemon_form_name' => $form_name, + 'pokemon_form_id' => $form_id, + 'min_cp' => $min_cp, + 'max_cp' => $max_cp, + 'min_weather_cp' => $min_weather_cp, + 'max_weather_cp' => $max_weather_cp, + 'weather' => $weather, + 'type' => $pokemon_types[0], + 'type2' => $pokemon_types[1], + ]; } - $highest = 0; - $url = ''; - foreach($leaf as $l) { - $version = trim(preg_replace('/\D/', '', substr($l['name'], 3))); - if($version > $highest) { - $split = explode(".",$l['name']); - // Only allow fully or partially deobfuscated iterations of the proto file - if($split[2] == 'x' or $split[2] == 'x_p_obf') { - $highest = $version; - $url = $l['download_url']; - } - } + if(!isset($row['tempEvolutions'])) continue; + foreach($row['tempEvolutions'] as $tempData) { + if(isset($tempData['unreleased']) && $tempData['unreleased']) continue; + $form_id = -$tempData['tempEvoId']; + $form_name = $mega_names[$form_id]; + if(isset($tempData['types'])) { + $pokemon_types = array_keys($tempData['types']); + $weather = $weatherboost_table[$pokemon_types[0]]; + if(!isset($pokemon_types[1])) + $pokemon_types[1] = ''; + elseif($weatherboost_table[$pokemon_types[0]] != $weatherboost_table[$pokemon_types[1]]) + $weather .= $weatherboost_table[$pokemon_types[1]]; + } + [$min_cp, $max_cp, $min_weather_cp, $max_weather_cp] = calculate_cps($row['stats']); + $pokemon_array[$pokemon_id][$form_name] = [ + 'pokemon_name' => $pokemon_name, + 'pokemon_form_name' => $form_name, + 'pokemon_form_id' => $form_id, + 'min_cp' => $min_cp, + 'max_cp' => $max_cp, + 'min_weather_cp' => $min_weather_cp, + 'max_weather_cp' => $max_weather_cp, + 'weather' => $weather, + 'type' => $pokemon_types[0], + 'type2' => $pokemon_types[1], + ]; } - return $url; + } + $costume_data = []; + foreach($master['costumes'] as $costume) { + $costume_data[$costume['proto']] = $costume['id']; + } + return [$pokemon_array, $costume_data]; } - -?> diff --git a/mods/gymMenu.php b/mods/gymMenu.php new file mode 100644 index 00000000..0562be60 --- /dev/null +++ b/mods/gymMenu.php @@ -0,0 +1,29 @@ +accessCheck('gym-add'); + +function insertUserInput($userId, $stage, $oldMessageId, $gymId = 0) { + global $dbh; + // Create an entry to user_input table + $modifierArray = ['stage' => $stage + 1, 'oldMessageId' => $oldMessageId]; + if($gymId !== 0) $modifierArray['gymId'] = $gymId; + $modifiers = json_encode($modifierArray); + $handler = 'gym_create'; + + my_query('INSERT INTO user_input SET user_id = :userId, modifiers = :modifiers, handler = :handler', [':userId' => $userId, ':modifiers' => $modifiers, ':handler' => $handler]); + return $dbh->lastInsertId(); +} +function respondToUser($userId, $oldMessageId = 0, $editMsg = '', $editKeys = [], $sendMsg = '', $sendKeys = [], $callbackMsg = '', $callbackId = 0) { + if($callbackId != 0) answerCallbackQuery($callbackId, $callbackMsg); + if($editMsg != '') editMessageText($oldMessageId, $editMsg, $editKeys, $userId, ['disable_web_page_preview' => 'true']); + if($sendMsg != '') send_message(create_chat_object([$userId]), $sendMsg, $sendKeys, ['disable_web_page_preview' => 'true']); +} +// Set keys. +$keys = []; + +$stage = $modifiers['stage'] ?? 1; + +if(isset($data['a'])) { + my_query('DELETE FROM user_input WHERE id = :deleteId', ['deleteId' => $data['a']]); + $msg = getTranslation('action_aborted'); + editMessageText($update['callback_query']['message']['message_id'], $msg, [], $update['callback_query']['from']['id']); + exit; +} +if($stage == 1) { + $callbackResponse = getTranslation('here_we_go'); + $callbackId = $update['callback_query']['id']; + + $userId = $update['callback_query']['from']['id']; + $oldMessageId = $update['callback_query']['message']['message_id']; + + $userInputId = insertUserInput($userId, $stage, $oldMessageId); + + $editMsg = getTranslation('gym_create') . ':'; + $editKeys[0][] = button(getTranslation('abort'), ['gym_create', 'a' => $userInputId]); + $sendMsg = EMOJI_HERE . getTranslation('gym_gps_instructions') . CR; + $sendMsg .= getTranslation('gym_gps_example'); + respondToUser($userId, $oldMessageId, $editMsg, $editKeys, $sendMsg, [], $callbackResponse, $callbackId); + exit; +} +$userId = $update['message']['from']['id']; +$oldMessageId = $modifiers['oldMessageId']; + +if($stage == 2) { + $input = $update['message']['text']; + $reg_exp_coordinates = '^[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?),\s*[-+]?(180(\.0+)?|((1[0-7]\d)|([1-9]?\d))(\.\d+)?)$^'; + if(preg_match($reg_exp_coordinates, $input)) { + [$lat,$lon] = explode(',', $input, 2); + my_query('INSERT INTO gyms (gym_name, lat, lon) VALUES (\'unknown\', :lat, :lon)', [':lat' => $lat, ':lon' => $lon]); + $gymId = $dbh->lastInsertId(); + + $userInputId = insertUserInput($userId, $stage, $oldMessageId, $gymId); + $msg = EMOJI_PENCIL . getTranslation('gym_name_instructions'); + respondToUser($userId, 0, '', [], $msg); + }else { + $msg = getTranslation('gym_gps_coordinates_format_error'); + respondToUser($userId, 0, '', [], $msg); + } + exit; +} +if($stage == 3) { + $input = trim($update['message']['text']); + if(strlen($input) <= 255) { + $gymId = $modifiers['gymId']; + my_query('UPDATE gyms SET gym_name = :gym_name WHERE id = :gymId', [':gym_name' => $input, ':gymId' => $gymId]); + + $msg = getTranslation('gym_added'); + $keys[][] = button(getTranslation('show_gym_details'), ['gym_details', 'g' => $gymId]); + respondToUser($userId, $oldMessageId, 'OK', [], $msg, $keys); + }else { + $msg = getTranslation('gym_edit_text_too_long'); + respondToUser($userId, 0, '', [], $msg); + } + exit; +} diff --git a/mods/gym_delete.php b/mods/gym_delete.php index ae2c5a31..1d0b6d86 100644 --- a/mods/gym_delete.php +++ b/mods/gym_delete.php @@ -1,74 +1,50 @@ accessCheck('gym-delete'); // Get the arg. -$arg = $data['arg']; +$gymId = $data['g']; +$confirm = $data['c'] == 1 ? true : false; -// Delete? -if(substr_count($arg, '-') == 1) { - $split_arg = explode('-', $arg); - $new_arg = $split_arg[0]; - $delete = true; - $confirm = false; -} else if(substr_count($arg, '-') == 2) { - $split_arg = explode('-', $arg); - $new_arg = $split_arg[0]; - $delete = true; - $confirm = true; -} else { - $msg = 'ERROR!'; - $keys = []; -} +$keys = []; +if ($gymId > 0 && $confirm == false) { + $gym = get_gym($gymId); -if ($new_arg > 0 && $delete == true && $confirm == false) { - $gym = get_gym($new_arg); - - // Set message - $msg = EMOJI_WARN . SP . '' . getTranslation('delete_this_gym') . '' . SP . EMOJI_WARN; - $msg .= CR . get_gym_details($gym); + // Set message + $msg = EMOJI_WARN . SP . '' . getTranslation('delete_this_gym') . '' . SP . EMOJI_WARN; + $msg .= CR . get_gym_details($gym); - // Create the keys. - $keys = [ - [ - [ - 'text' => getTranslation('yes'), - 'callback_data' => '0:gym_delete:' . $new_arg . '-delete-yes' - ] - ], - [ - [ - 'text' => getTranslation('no'), - 'callback_data' => '0:exit:0' - ] - ] - ]; + // Create the keys. + $keys[][] = button(getTranslation('yes'), ['gym_delete', 'g' => $gymId, 'c' => 1]); + $keys[][] = button(getTranslation('no'), ['gym_edit_details', 'g' => $gymId]); // Delete the gym. -} else if ($new_arg > 0 && $delete == true && $confirm == true) { - debug_log('Deleting gym with ID ' . $new_arg); - // Get gym. - $gym = get_gym($new_arg); - - // Set message - $msg = '' . getTranslation('deleted_this_gym') . '' . CR; - $msg .= get_gym_details($gym); - $keys = []; - - // Delete gym. - my_query( - " - DELETE FROM gyms - WHERE id = {$new_arg} - " - ); +} else if ($gymId > 0 && $confirm == true) { + require_once(LOGIC_PATH . '/get_gym_details.php'); + require_once(LOGIC_PATH . '/get_gym.php'); + debug_log('Deleting gym with ID ' . $gymId); + // Get gym. + $gym = get_gym($gymId); + + // Set message + $msg = '' . getTranslation('deleted_this_gym') . '' . CR; + $msg .= get_gym_details($gym); + + // Delete gym. + my_query(' + DELETE FROM gyms + WHERE id = ? + ', [$gymId] + ); } // Build callback message string. @@ -85,6 +61,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/gym_details.php b/mods/gym_details.php index 58279cba..2c091497 100644 --- a/mods/gym_details.php +++ b/mods/gym_details.php @@ -1,110 +1,28 @@ accessCheck('gym-details'); // Get the arg. -$args = explode(',',$data['arg'],2); -$arg = $args[0]; -$gymarea_id = (count($args) > 1) ? $args[1] : false; +$arg = $data['g']; // Get the id. -$id = $data['id']; - -// ID or Arg = 0 ? -if($arg == 0 || $id == '0' || $id == '1') { - // Get hidden gyms? - if($id == 0) { - $hidden = true; - } else { - $hidden = false; - } - - // Get the keys. - $keys = raid_edit_gym_keys($arg, $gymarea_id, 'gym_details', false, $hidden); - - // Set keys. - $msg = '' . getTranslation('show_gym_details') . CR . CR . getTranslation('select_gym_name') . ''; - - // No keys found. - if (!$keys) { - // Create the keys. - $keys = [ - [ - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ] - ]; - } else { - // Add navigation keys. - $nav_keys = []; - $nav_keys[] = universal_inner_key($nav_keys, $gymarea_id, 'gym_letter', 'gym_details', getTranslation('back')); - $nav_keys[] = universal_inner_key($nav_keys, '0', 'exit', '0', getTranslation('abort')); - $nav_keys = inline_key_array($nav_keys, 2); - // Merge keys. - $keys = array_merge($keys, $nav_keys); - } +$id = $data['g']; // Get gym info. -} else { - $gym = get_gym($arg); - $msg = get_gym_details($gym, true); - $msg .= CR . CR . '' . getTranslation('change_extended_gym_details') . ''; - - // Hide gym? - if($gym['show_gym'] == 1) { - $text_show_button = getTranslation('hide_gym'); - $arg_show = 0; +$gym = get_gym($arg); +$msg = get_gym_details($gym, true); - // Show gym? - } else { - $text_show_button = getTranslation('show_gym'); - $arg_show = 1; - } - - // Normal gym? - if($gym['ex_gym'] == 1) { - $text_ex_button = getTranslation('normal_gym'); - $arg_ex = 0; - - // Ex-raid gym? - } else { - $text_ex_button = getTranslation('ex_gym'); - $arg_ex = 1; - } - - // Add buttons to show/hide the gym and add/remove ex-raid flag - $keys = []; - $keys[] = array( - 'text' => $text_show_button, - 'callback_data' => $arg . ':gym_edit_details:show-' . $arg_show - ); - $keys[] = array( - 'text' => $text_ex_button, - 'callback_data' => $arg . ':gym_edit_details:ex-' . $arg_ex - ); - if(bot_access_check($update, 'gym-delete', true)) { - $keys[] = array( - 'text' => getTranslation("gym_delete"), - 'callback_data' => '0:gym_delete:'.$arg.'-delete' - ); - } - $keys[] = array( - 'text' => getTranslation('done'), - 'callback_data' => '0:exit:1' - ); - - // Get the inline key array. - $keys = inline_key_array($keys, 1); -} +$keys = edit_gym_keys($update, $arg, $gym['show_gym'], $gym['ex_gym'], $gym['gym_note'], $gym['address']); // Build callback message string. $callback_response = getTranslation('here_we_go'); @@ -120,6 +38,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/gym_edit_details.php b/mods/gym_edit_details.php index 20842d7f..862f50e1 100644 --- a/mods/gym_edit_details.php +++ b/mods/gym_edit_details.php @@ -1,86 +1,91 @@ accessCheck('gym-edit'); // Get the id. -$id = $data['id']; - -// Get the arg. -$arg = $data['arg']; +$gym_id = $data['g']; // Split the arg. -$split_arg = explode('-', $arg); -$action = $split_arg[0]; -$value = $split_arg[1]; +$action = $data['a'] ?? ''; +$value = $data['v'] ?? false; +$delete_id = $data['d'] ?? false; // Set keys. $keys = []; -// Update gym info. -if($action == 'show' || $action == 'ex') { - $gym = get_gym($id); - - // Set message - $msg = get_gym_details($gym, true); - $msg .= CR . CR . '' . getTranslation('new_extended_gym_detail') . ''; - - // New extended gym detail. - if($action == 'show' && $value == 0) { - $msg .= CR . '-' . SP . getTranslation('hide_gym'); - } else if($action == 'show' && $value == 1) { - $msg .= CR . '-' . SP . getTranslation('show_gym'); - } else if($action == 'ex' && $value == 0) { - $msg .= CR . '-' . SP . getTranslation('normal_gym'); - } else if($action == 'ex' && $value == 1) { - $msg .= CR . '-' . SP . getTranslation('ex_gym'); +debug_log('Changing the details for the gym with ID ' . $gym_id); + +$gym = get_gym($gym_id); + +// Did we receive a call to edit some gym data that requires a text input +if(in_array($action, ['name','note','gps','addr'])) { + if($value == 'd') { + my_query('DELETE FROM user_input WHERE id = :id', ['id' => $delete_id]); + if($action == 'note') { + my_query('UPDATE gyms SET gym_note = NULL WHERE id = :id', ['id' => $gym_id]); + $gym['gym_note'] = ''; } - $msg .= CR . CR . '' . getTranslation('change_extended_gym_details') . ''; - - // Create the keys. - $keys = [ - [ - [ - 'text' => getTranslation('yes'), - 'callback_data' => $id . ':gym_edit_details:' . 'confirm' . $action . '-' . $value - ] - ], - [ - [ - 'text' => getTranslation('no'), - 'callback_data' => '0:exit:0' - ] - ] - ]; - -} else if($action == 'confirmshow' || $action == 'confirmex') { - debug_log('Changing the details for the gym with ID ' . $id); - // Show or ex? - $table = 'show_gym'; - if($action == 'confirmex') { - $table = 'ex_gym'; + $msg = get_gym_details($gym, true); + $keys = edit_gym_keys($update, $gym_id, $gym['show_gym'], $gym['ex_gym'], $gym['gym_note'], $gym['address']); + }elseif($value == 'e') { + my_query('DELETE FROM user_input WHERE id = ?', [$delete_id]); + if($action == 'addr') { + $addr = format_address(get_address($gym['lat'], $gym['lon'])); + my_query('UPDATE gyms SET address = :addr WHERE id = :id', ['addr' => $addr, 'id' => $gym_id]); + $gym['address'] = $addr; } + $msg = get_gym_details($gym, true); + $keys = edit_gym_keys($update, $gym_id, $gym['show_gym'], $gym['ex_gym'], $gym['gym_note'], $gym['address']); + }else { + // Create an entry to user_input table + $userid = $update['callback_query']['from']['id']; + $modifiers = json_encode(array('id' => $gym_id, 'value' => $action, 'old_message_id' => $update['callback_query']['message']['message_id'])); + $handler = 'save_gym_info'; - my_query( - " - UPDATE gyms - SET $table = $value - WHERE id = {$id} - " - ); + my_query('INSERT INTO user_input SET user_id = :userid, modifiers = :modifiers, handler = :handler', [':userid' => $userid, ':modifiers' => $modifiers, ':handler' => $handler]); - // Get gym. - $gym = get_gym($id); - - // Set message. - $msg = '' . getTranslation('gym_saved') . ''; - $msg .= CR . get_gym_details($gym, true); + $msg = get_gym_details($gym, true); + if($action == 'addr') $instructions = 'gym_address_instructions'; else $instructions = 'gym_'.$action.'_instructions'; + $msg .= CR . CR . '' . getTranslation($instructions) . ''; + if($action == 'gps') $msg .= CR. getTranslation('gym_gps_example'); + + $keys[0][] = button(getTranslation('abort'), ['gym_edit_details', 'g' => $gym_id, 'a' => 'abort', 'd' => $dbh->lastInsertId()]); + if($action == 'note' && !empty($gym['gym_note'])) { + $keys[0][] = button(getTranslation('delete'), ['gym_edit_details', 'g' => $gym_id, 'a' => 'note', 'd' => $dbh->lastInsertId()]); + } + if($action == 'addr') { + $keys[0][] = button(getTranslation('gym_save_lookup_result'), ['gym_edit_details', 'g' => $gym_id, 'a' => 'addr', 'd' => $dbh->lastInsertId()]); + } + } +}else { + if($action == 'show') { + $table = 'show_gym'; + }else if($action == 'ex') { + $table = 'ex_gym'; + }else if($action == 'abort') { + my_query('DELETE FROM user_input WHERE id = :delete_id', ['delete_id' => $delete_id]); + } + if(isset($table)) { + my_query(' + UPDATE gyms + SET ' . $table . ' = :value + WHERE id = :gym_id + ', ['value' => $value, 'gym_id' => $gym_id] + ); + $gym[$table] = $value; + } + $msg = get_gym_details($gym, true); + $keys = edit_gym_keys($update, $gym_id, $gym['show_gym'], $gym['ex_gym'], $gym['gym_note'], $gym['address']); } // Build callback message string. @@ -97,6 +102,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/gym_hidden_letter.php b/mods/gym_hidden_letter.php deleted file mode 100644 index 716fc069..00000000 --- a/mods/gym_hidden_letter.php +++ /dev/null @@ -1,67 +0,0 @@ -' . getTranslation('gym_delete') . SP . '—' . SP . getTranslation('select_gym_first_letter') . ''; -} else { - // Force set arg. - $arg = 'gym_details'; - - // Check access. - bot_access_check($update, 'gym-details'); - - // Set message. - $msg = '' . getTranslation('show_gym_details') . SP . '—' . SP . getTranslation('select_gym_first_letter') . ''; -} - -// Set keys. - -$keys_and_gymarea = raid_edit_gyms_first_letter_keys($arg, true, $data['id'], 'gym_letter', 'gym_details'); -$keys = $keys_and_gymarea['keys']; - -// Set message. -if(!$keys) { - $msg = CR . '' . getTranslation('no_hidden_gyms') . ''; -} - -// Add navigation keys. -$nav_keys = []; -$nav_keys[] = universal_inner_key($nav_keys, $data['id'], 'gym_letter', $arg, getTranslation('back')); -$nav_keys[] = universal_inner_key($nav_keys, '0', 'exit', '0', getTranslation('abort')); -$nav_keys = inline_key_array($nav_keys, 2); - -// Merge keys. -$keys = array_merge($keys, $nav_keys); - -// Build callback message string. -$callback_response = getTranslation('here_we_go'); - -// Telegram JSON array. -$tg_json = array(); - -// Answer callback. -$tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_response, true); - -// Edit the message. -$tg_json[] = edit_message($update, $msg, $keys, ['disable_web_page_preview' => 'true'], true); - -// Telegram multicurl request. -curl_json_multi_request($tg_json); - -// Exit. -exit(); - -?> diff --git a/mods/gym_letter.php b/mods/gym_letter.php deleted file mode 100644 index 1bb15303..00000000 --- a/mods/gym_letter.php +++ /dev/null @@ -1,76 +0,0 @@ -' . getTranslation('gym_delete') . CR . getTranslation('select_gym_first_letter') . ''; - $msg.= (($keys_and_gymarea['gymarea_name'] != '') ? CR . CR . getTranslation('current_gymarea') . ': ' . $keys_and_gymarea['gymarea_name'] : ''); -} else { - // Force set arg. - $arg = 'gym_details'; - - // Check access. - bot_access_check($update, 'gym-details'); - - // Set message. - $msg = '' . getTranslation('show_gym_details') . CR . getTranslation('select_gym_first_letter') . ''; - $msg.= (($keys_and_gymarea['gymarea_name'] != '') ? CR . CR . getTranslation('current_gymarea') . ': ' . $keys_and_gymarea['gymarea_name'] : ''); -} - -$nav_keys = []; - -if($data['id'] != 'n' or $config->ENABLE_GYM_AREAS === false) { - $nav_keys[] = [ - 'text' => getTranslation('back'), - 'callback_data' => 'n:gym_letter:gym_details' - ]; - // Add key for hidden gyms. - $h_keys = []; - $h_keys[] = universal_inner_key($h_keys, $data['id'], 'gym_hidden_letter', $arg, getTranslation('hidden_gyms')); - $h_keys = inline_key_array($h_keys, 1); - // Merge keys. - $keys = array_merge($h_keys, $keys); -} -$nav_keys[] = [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ]; -$nav_keys = inline_key_array($nav_keys, 2); -// Merge keys. -$keys = array_merge($keys, $nav_keys); - -// Build callback message string. -$callback_response = getTranslation('here_we_go'); - -// Telegram JSON array. -$tg_json = array(); - -// Answer callback. -$tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_response, true); - -// Edit the message. -$tg_json[] = edit_message($update, $msg, $keys, ['disable_web_page_preview' => 'true'], true); - -// Telegram multicurl request. -curl_json_multi_request($tg_json); - -// Exit. -exit(); - -?> diff --git a/mods/history.php b/mods/history.php index a895c989..331beee3 100644 --- a/mods/history.php +++ b/mods/history.php @@ -7,84 +7,73 @@ //debug_log($data); // Check access. -bot_access_check($update, 'history'); +$botUser->accessCheck('history'); // Expected callback data: [Day number (0-31), DD]:history:[Year and month, YYYY-MM] require_once(LOGIC_PATH .'/history.php'); -$current_day = $data['id']; -$current_year_month = $data['arg']; +$current_day = $data['d'] ?? 0; +$current_year_month = $data['m'] ?? ''; if($current_day == 0) { - $msg_keys = create_history_date_msg_keys($current_year_month); - $msg = $msg_keys[0]; - $keys = $msg_keys[1]; + $msg_keys = create_history_date_msg_keys($current_year_month); + $msg = $msg_keys[0]; + $keys = $msg_keys[1]; }else { - $msg = getTranslation('history_title') . CR . CR; - $msg.= '' . getTranslation('date') . ': ' . getTranslation('month_' . substr($current_year_month,5)) . ' ' . $current_day . CR . CR; - $msg.= getTranslation('select_gym_first_letter'); - // Special/Custom gym letters? - if(!empty($config->RAID_CUSTOM_GYM_LETTERS)) { - // Explode special letters. - $special_keys = explode(',', $config->RAID_CUSTOM_GYM_LETTERS); - $select_query = 'CASE'; - foreach($special_keys as $id => $letter) - { - $letter = trim($letter); - debug_log($letter, 'Special gym letter:'); - // Fix chinese chars, prior: $length = strlen($letter); - $length = strlen(utf8_decode($letter)); - $select_query .= SP . "WHEN UPPER(LEFT(gym_name, " . $length . ")) = '" . $letter . "' THEN UPPER(LEFT(gym_name, " . $length . "))" . SP; - } - $select_query .= 'ELSE UPPER(LEFT(gym_name, 1)) END'; - }else { - $select_query = 'DISTINCT UPPER(SUBSTR(gym_name, 1, 1))'; + $msg = getTranslation('history_title') . CR . CR; + $msg.= '' . getTranslation('date') . ': ' . getTranslation('month_' . substr($current_year_month,5)) . ' ' . $current_day . CR . CR; + $msg.= getTranslation('select_gym_first_letter'); + // Special/Custom gym letters? + if(!empty($config->RAID_CUSTOM_GYM_LETTERS)) { + // Explode special letters. + $special_keys = explode(',', $config->RAID_CUSTOM_GYM_LETTERS); + $select_query = 'CASE'; + foreach($special_keys as $id => $letter) + { + $letter = trim($letter); + debug_log($letter, 'Special gym letter:'); + // Fix chinese chars, prior: $length = strlen($letter); + $length = strlen(mb_convert_encoding($letter, 'ISO-8859-1')); + $select_query .= SP . "WHEN UPPER(LEFT(gym_name, " . $length . ")) = '" . $letter . "' THEN UPPER(LEFT(gym_name, " . $length . "))" . SP; } - $date = $current_year_month.'-'.$current_day; - - $rs = my_query( - ' - SELECT '.$select_query.' AS first_letter - FROM raids - LEFT JOIN gyms - ON raids.gym_id = gyms.id - LEFT JOIN attendance - ON attendance.raid_id = raids.id - WHERE date_format(start_time, "%Y-%m-%d") = \''.$date.'\' - AND raids.end_time < UTC_TIMESTAMP() - AND attendance.id IS NOT NULL - AND gyms.gym_name IS NOT NULL - ORDER BY first_letter - ' - ); - - // Init empty keys array. - $keys = []; - - while ($gym = $rs->fetch()) { + $select_query .= 'ELSE UPPER(LEFT(gym_name, 1)) END'; + }else { + $select_query = 'DISTINCT UPPER(SUBSTR(gym_name, 1, 1))'; + } + $date = $current_year_month.'-'.$current_day; + + $rs = my_query(' + SELECT '.$select_query.' AS first_letter + FROM raids + LEFT JOIN gyms + ON raids.gym_id = gyms.id + LEFT JOIN attendance + ON attendance.raid_id = raids.id + WHERE date_format(start_time, "%Y-%m-%d") = ? + AND raids.end_time < UTC_TIMESTAMP() + AND attendance.id IS NOT NULL + AND gyms.gym_name IS NOT NULL + ORDER BY first_letter + ', [$date]); + + // Init empty keys array. + $keys = []; + + while ($gym = $rs->fetch()) { // Add first letter to keys array - $keys[] = array( - 'text' => $gym['first_letter'], - 'callback_data' => $date . ':history_gyms:' . $gym['first_letter'] - ); - } - // Format buttons - $keys = inline_key_array($keys, 4); - - $nav_keys = [ - [ - 'text' => getTranslation('back'), - 'callback_data' => '0:history:' . $current_year_month - ], - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ]; - - // Get the inline key array. - $keys[] = $nav_keys; + $keys[] = button($gym['first_letter'],['history_gyms', 'd' => $date, 'fl' => $gym['first_letter']]); + } + // Format buttons + $keys = inline_key_array($keys, 4); + + $nav_keys = [ + button(getTranslation('back'), ['history', 'm' => $current_year_month]), + button(getTranslation('abort'), 'exit') + ]; + + // Get the inline key array. + $keys[] = $nav_keys; } @@ -98,7 +87,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); -?> diff --git a/mods/history_gyms.php b/mods/history_gyms.php index 10c3e701..ebea6a5d 100644 --- a/mods/history_gyms.php +++ b/mods/history_gyms.php @@ -7,12 +7,12 @@ //debug_log($data); // Check access. -bot_access_check($update, 'history'); +$botUser->accessCheck('history'); // Expected callback data: [Date, YYYY-MM-DD]:history_gyms:[GYM_LETTER] -$current_date = $data['id']; -$first = $data['arg']; +$current_date = $data['d']; +$first = $data['fl']; $split_date = explode('-', $current_date); $current_day = $split_date[2]; @@ -20,69 +20,58 @@ // Length of first letter. // Fix chinese chars, prior: $first_length = strlen($first); -$first_length = strlen(utf8_decode($first)); +$first_length = strlen(mb_convert_encoding($first, 'ISO-8859-1')); // Special/Custom gym letters? $not = ''; if(!empty($config->RAID_CUSTOM_GYM_LETTERS) && $first_length == 1) { - // Explode special letters. - $special_keys = explode(',', $config->RAID_CUSTOM_GYM_LETTERS); - - foreach($special_keys as $id => $letter) - { - $letter = trim($letter); - debug_log($letter, 'Special gym letter:'); - // Fix chinese chars, prior: $length = strlen($letter); - $length = strlen(utf8_decode($letter)); - $not .= SP . "AND UPPER(LEFT(gym_name, " . $length . ")) != UPPER('" . $letter . "')" . SP; - } + // Explode special letters. + $special_keys = explode(',', $config->RAID_CUSTOM_GYM_LETTERS); + + foreach($special_keys as $id => $letter) + { + $letter = trim($letter); + debug_log($letter, 'Special gym letter:'); + // Fix chinese chars, prior: $length = strlen($letter); + $length = strlen(mb_convert_encoding($letter, 'ISO-8859-1')); + $not .= SP . "AND UPPER(LEFT(gym_name, " . $length . ")) != UPPER('" . $letter . "')" . SP; + } } $query_collate = ""; -if($config->MYSQL_SORT_COLLATE != "") { - $query_collate = "COLLATE " . $config->MYSQL_SORT_COLLATE; +if($config->MYSQL_SORT_COLLATE != '') { + $query_collate = 'COLLATE ' . $config->MYSQL_SORT_COLLATE; } // Get gyms from database -$rs = my_query( - ' - SELECT gyms.id, gyms.gym_name, gyms.ex_gym - FROM gyms - LEFT JOIN raids - ON raids.gym_id = gyms.id - LEFT JOIN attendance - ON attendance.raid_id = raids.id - WHERE UPPER(LEFT(gym_name, ' . $first_length . ')) = UPPER("' . $first . '") - AND date_format(start_time, "%Y-%m-%d") = "' . $current_date . '" - AND raids.end_time < UTC_TIMESTAMP() - AND attendance.id IS NOT NULL - ' . $not . ' - GROUP BY gym_name, raids.gym_id, gyms.id, gyms.ex_gym - ORDER BY gym_name ' . $query_collate - +$rs = my_query(' + SELECT gyms.id, gyms.gym_name, gyms.ex_gym + FROM gyms + LEFT JOIN raids + ON raids.gym_id = gyms.id + LEFT JOIN attendance + ON attendance.raid_id = raids.id + WHERE UPPER(LEFT(gym_name, ' . $first_length . ')) = UPPER("' . $first . '") + AND date_format(start_time, "%Y-%m-%d") = "' . $current_date . '" + AND raids.end_time < UTC_TIMESTAMP() + AND attendance.id IS NOT NULL + ' . $not . ' + GROUP BY gym_name, raids.gym_id, gyms.id, gyms.ex_gym + ORDER BY gym_name ' . $query_collate ); while ($gym = $rs->fetch()) { - // Show Ex-Gym-Marker? - if($config->RAID_CREATION_EX_GYM_MARKER && $gym['ex_gym'] == 1) { - $ex_raid_gym_marker = (strtolower($config->RAID_EX_GYM_MARKER) == 'icon') ? EMOJI_STAR : $config->RAID_EX_GYM_MARKER; - $gym_name = $ex_raid_gym_marker . SP . $gym['gym_name']; - } else { - $gym_name = $gym['gym_name']; - } - $keys[][] = [ - 'text' => $gym_name, - 'callback_data' => $current_date . '/' . $first . ':history_raids:' . $gym['id'] - ]; + // Show Ex-Gym-Marker? + if($config->RAID_CREATION_EX_GYM_MARKER && $gym['ex_gym'] == 1) { + $ex_raid_gym_marker = (strtolower($config->RAID_EX_GYM_MARKER) == 'icon') ? EMOJI_STAR : $config->RAID_EX_GYM_MARKER; + $gym_name = $ex_raid_gym_marker . SP . $gym['gym_name']; + } else { + $gym_name = $gym['gym_name']; + } + $keys[][] = button($gym_name, ['history_raids', 'd' => $current_date, 'fl' => $first, 'g' => $gym['id']]); } $nav_keys = [ - [ - 'text' => getTranslation('back'), - 'callback_data' => $current_day.':history:' . $current_year_month - ], - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] + button(getTranslation('back'), ['history', 'd' => $current_day, 'm' => $current_year_month]), + button(getTranslation('abort'), 'exit') ]; $keys[] = $nav_keys; @@ -99,5 +88,3 @@ curl_json_multi_request($tg_json); exit(); - -?> \ No newline at end of file diff --git a/mods/history_raid.php b/mods/history_raid.php index 5b518abe..dcd519a8 100644 --- a/mods/history_raid.php +++ b/mods/history_raid.php @@ -1,19 +1,16 @@ accessCheck('history'); -// Expected callback data: [Date, YYYY-MM-DD]/[GYM_LETTER]:history_raid:[GYM_ID]/[RAID_ID] - -$arg_data = explode('/',$data['arg']); -$gym_id = $arg_data[0]; -$raid_id = $arg_data[1]; +$raid_id = $data['r']; $raid = get_raid($raid_id); @@ -25,15 +22,11 @@ // Answer callback. $tg_json[] = answerCallbackQuery($update['callback_query']['id'], 'OK', true); +$backData = $data; +$backData[0] = 'history_raids'; $keys[] = [ - [ - 'text' => getTranslation('back'), - 'callback_data' => $data['id'] . ':history_raids:' . $gym_id - ], - [ - 'text' => getTranslation('done'), - 'callback_data' => '0:exit:1' - ] + button(getTranslation('back'), $backData), + button(getTranslation('done'), ['exit', 'd' => '1']) ]; // Edit message. @@ -41,8 +34,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); - -?> \ No newline at end of file diff --git a/mods/history_raids.php b/mods/history_raids.php index 42cb6c27..996462e3 100644 --- a/mods/history_raids.php +++ b/mods/history_raids.php @@ -7,49 +7,38 @@ //debug_log($data); // Check access. -bot_access_check($update, 'history'); +$botUser->accessCheck('history'); -// Expected callback data: [Date, YYYY-MM-DD]/[GYM_LETTER]:history_raids:[GYM_ID] - -$id_data = explode('/',$data['id']); -$current_date = $id_data[0]; -$gym_first_letter = $id_data[1]; - -$gym_id = $data['arg']; +$current_date = $data['d']; +$gym_first_letter = $data['fl']; +$gym_id = $data['g']; // Get raids from database -$rs = my_query( - ' - SELECT gyms.gym_name, raids.id, raids.start_time, raids.pokemon, raids.pokemon_form - FROM gyms - LEFT JOIN raids - ON raids.gym_id = gyms.id - LEFT JOIN attendance - ON attendance.raid_id = raids.id - WHERE gyms.id = "'.$gym_id.'" - AND raids.end_time < UTC_TIMESTAMP() - AND attendance.id IS NOT NULL - GROUP BY raids.id, raids.start_time, raids.pokemon, raids.pokemon_form, gyms.gym_name - ORDER BY start_time - ' +$rs = my_query(' + SELECT gyms.gym_name, raids.id, raids.start_time, raids.pokemon, raids.pokemon_form + FROM gyms + LEFT JOIN raids + ON raids.gym_id = gyms.id + LEFT JOIN attendance + ON attendance.raid_id = raids.id + WHERE gyms.id = ? + AND raids.end_time < UTC_TIMESTAMP() + AND attendance.id IS NOT NULL + GROUP BY raids.id, raids.start_time, raids.pokemon, raids.pokemon_form, gyms.gym_name + ORDER BY start_time + ', [$gym_id] ); while ($raid = $rs->fetch()) { - $keys[][] = [ - 'text' => dt2time($raid['start_time']) . ': ' . get_local_pokemon_name($raid['pokemon'],$raid['pokemon_form']), - 'callback_data' => $data['id'] . ':history_raid:' . $gym_id .'/' . $raid['id'] - ]; - $gym_name = $raid['gym_name']; - $start_time = $raid['start_time']; + $newData = $data; + $newData[0] = 'history_raid'; + $newData['r'] = $raid['id']; + $keys[][] = button(dt2time($raid['start_time']) . ': ' . get_local_pokemon_name($raid['pokemon'],$raid['pokemon_form']), $newData); + $gym_name = $raid['gym_name']; + $start_time = $raid['start_time']; } $nav_keys = [ - [ - 'text' => getTranslation('back'), - 'callback_data' => $current_date . ':history_gyms:' . $gym_first_letter - ], - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] + button(getTranslation('back'), ['history_gyms', 'd' => $current_date, 'fl' => $gym_first_letter]), + button(getTranslation('abort'), 'exit') ]; $keys[] = $nav_keys; @@ -67,5 +56,3 @@ curl_json_multi_request($tg_json); exit(); - -?> \ No newline at end of file diff --git a/mods/import_future_bosses.php b/mods/import_future_bosses.php index ac2b1e9a..25462d3d 100644 --- a/mods/import_future_bosses.php +++ b/mods/import_future_bosses.php @@ -7,77 +7,54 @@ //debug_log($data); // Check access. -bot_access_check($update, 'pokedex'); +$botUser->accessCheck('pokedex'); require_once(LOGIC_PATH . '/read_upcoming_bosses.php'); -$id = $data['id']; -$arg = $data['arg']; +$action = $data['a'] ?? 0; -if($arg == '1') { - try { - $sql = 'DELETE FROM raid_bosses WHERE scheduled = 1;'; - $sql .= read_upcoming_bosses(true); - $query = $dbh->prepare($sql); - $query->execute(); - $msg = getTranslation('import_done'); - }catch (PDOException $exception) { - $msg = getTranslation('internal_error') . CR; - $msg.= $exception->getMessage(); - info_log($exception->getMessage()); +if($action == '1') { + $sql = 'DELETE FROM raid_bosses WHERE scheduled = 1;'; + $sql .= read_upcoming_bosses('sql'); + $query = my_query($sql); + $msg = getTranslation('import_done'); + $tg_json = array(); + $tg_json[] = answerCallbackQuery($update['callback_query']['id'], 'OK', true); + $tg_json[] = edit_message($update, $msg, [], false, true); + curl_json_multi_request($tg_json); + exit(); +} +$list = read_upcoming_bosses(); +$msg = ''; +if(!empty($list)) { + $now = new DateTime('now', new DateTimeZone($config->TIMEZONE)); + $query = my_query(" + SELECT id, pokedex_id, pokemon_form_id, raid_level, scheduled, DATE_FORMAT(date_start, '%e.%c. ".getTranslation('raid_egg_opens_at')." %H:%i') as date_start, DATE_FORMAT(date_end, '%e.%c. ".getTranslation('raid_egg_opens_at')." %H:%i') as date_end FROM raid_bosses + WHERE date_end > '" . $now->format('Y-m-d H:i:s') . "' + AND scheduled = 1 + ORDER BY date_start, raid_level, pokedex_id, pokemon_form_id + "); + $prev_start = $prev_rl = ''; + $msg = '' . getTranslation('current_scheduled_bosses') . ':'; + foreach($query->fetchAll() as $result) { + if($prev_start != $result['date_start']) { + $msg.= CR . EMOJI_CLOCK . ' ' . $result['date_start'] . ' — ' . $result['date_end'] . ':' . CR; + $prev_rl = ''; } - $keys = []; -}else { - $list = read_upcoming_bosses(); - $msg = ''; - if(!empty($list)) { - $now = new DateTime('now', new DateTimeZone($config->TIMEZONE)); - $query = my_query(" - SELECT * FROM raid_bosses - WHERE date_end > '" . $now->format('Y-m-d H:i:s') . "' - AND scheduled = 1 - ORDER BY date_start, raid_level, pokedex_id, pokemon_form_id - "); - $prev_start = $prev_rl = ''; - $msg = '' . getTranslation('current_scheduled_bosses') . ':'; - foreach($query->fetchAll() as $result) { - if($prev_start != $result['date_start']) { - $msg.= CR . '' . $result['date_start'] . ' - ' . $result['date_end'] . ':' . CR; - } - if($prev_rl != $result['raid_level']) { - $msg.= '' . getTranslation($result['raid_level'] . 'stars') .':' . CR; - } - $msg.= get_local_pokemon_name($result['pokedex_id'], $result['pokemon_form_id']) . CR; - $prev_start = $result['date_start']; - $prev_rl = $result['raid_level']; - } - $msg .= CR . CR . '' . getTranslation('found_upcoming_bosses') . ':'; - $msg .= $list; - $msg .= CR . CR . getTranslation('confirm_replace_upcoming'); - $keys = [ - [ - [ - 'text' => getTranslation('replace'), - 'callback_data' => '1:import_future_bosses:1' - ] - ], - [ - [ - 'text'=>getTranslation('back'), - 'callback_data' => '0:pokedex_import:0' - ] - ] - ]; - }else { - $msg .= getTranslation('upcoming_bosses_not_found'); - $keys = [ - [ - [ - 'text' => getTranslation('done'), - 'callback_data' => '0:exit:1' - ] - ] - ]; + if($prev_rl != $result['raid_level']) { + $msg.= '' . getTranslation($result['raid_level'] . 'stars') .':' . CR; } + $msg.= get_local_pokemon_name($result['pokedex_id'], $result['pokemon_form_id']) . CR; + $prev_start = $result['date_start']; + $prev_rl = $result['raid_level']; + } + $msg .= CR . CR . '' . getTranslation('found_upcoming_bosses') . ':'; + $msg .= $list; + $msg .= CR . CR . getTranslation('confirm_replace_upcoming'); + $keys[][] = button(getTranslation('replace'), ['import_future_bosses', 'a' => 1]); + $keys[][] = button(getTranslation('back'), 'pokedex_import'); +}else { + $msg .= getTranslation('upcoming_bosses_not_found'); + $keys[][] = button(getTranslation('done'), ['exit', 'd' => '1']); } // Callback message string. @@ -94,8 +71,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -$dbh = null; -exit(); - -?> \ No newline at end of file diff --git a/mods/import_shinyinfo.php b/mods/import_shinyinfo.php index bc5c1217..233de82d 100644 --- a/mods/import_shinyinfo.php +++ b/mods/import_shinyinfo.php @@ -1,22 +1,23 @@ accessCheck('pokedex'); $link = 'https://fight.pokebattler.com/raids'; $pb_data = curl_get_contents($link); +if($pb_data === false) { + $callback_response = getTranslation('internal_error'); + answerCallbackQuery($update['callback_query']['id'], $callback_response); + exit(); +} $pb_data = json_decode($pb_data,true); // Init empty keys array. @@ -24,107 +25,34 @@ $msg = ''; $shinydata = []; foreach($pb_data['tiers'] as $tier) { - - // Raid level and message. - $rl = str_replace('RAID_LEVEL_','', $tier['tier']); - if($rl == "MEGA") $raid_level_id = 6; else $raid_level_id = $rl; - $rl_parts = explode('_', $rl); - if($rl_parts[count($rl_parts)-1] == 'FUTURE') continue; - #$msg .= '' . getTranslation('pokedex_raid_level') . SP . $rl . ':' . CR; - - // Get raid bosses for each raid level. - foreach($tier['raids'] as $raid) { - if(!isset($raid['pokemon']) || $raid['shiny'] != 'true') continue; - // Pokemon name ending with "_FORM" ? - if(substr_compare($raid['pokemon'], '_FORM', -strlen('_FORM')) === 0) { - debug_log('Pokemon with a special form received: ' . $raid['pokemon']); - // Remove "_FORM" - $pokemon = str_replace('_FORM', '', $raid['pokemon']); - - // Get pokemon name and form. - $name = explode("_", $pokemon, 2)[0]; - $form = explode("_", $pokemon, 2)[1]; - - // Fix for MEWTWO_A_FORM - if($name == 'MEWTWO' && $form == 'A') { - $form = 'ARMORED'; - } - - // Pokemon name ending with "_MALE" ? - } else if(substr_compare($raid['pokemon'], '_MALE', -strlen('_MALE')) === 0) { - debug_log('Pokemon with gender MALE received: ' . $raid['pokemon']); - // Remove "_MALE" - $pokemon = str_replace('_MALE', '', $raid['pokemon']); - - // Get pokemon name and form. - $name = explode("_", $pokemon, 2)[0] . '♂'; - $form = 'normal'; - - // Pokemon name ending with "_FEMALE" ? - } else if(substr_compare($raid['pokemon'], '_FEMALE', -strlen('_FEMALE')) === 0) { - debug_log('Pokemon with gender FEMALE received: ' . $raid['pokemon']); - // Remove "_FEMALE" - $pokemon = str_replace('_FEMALE', '', $raid['pokemon']); - - // Get pokemon name and form. - $name = explode("_", $pokemon, 2)[0] . '♀'; - $form = 'normal'; - - // Mega pokemon ? - }else if(substr_compare($raid['pokemon'], '_MEGA', -strlen('_MEGA')) === 0 or substr_compare($raid['pokemon'], '_MEGA_X', -strlen('_MEGA_X')) === 0 or substr_compare($raid['pokemon'], '_MEGA_Y', -strlen('_MEGA_Y')) === 0) { - debug_log('Mega Pokemon received: ' . $raid['pokemon']); - - // Get pokemon name and form. - $name_form = explode("_", $raid['pokemon'], 2); - $name = $name_form[0]; - $form = $name_form[1]; - - // Normal pokemon without form or gender. - } else { - // Fix pokemon like "HO_OH"... - if(substr_count($raid['pokemon'], '_') >= 1) { - $pokemon = str_replace('_', '-', $raid['pokemon']); - } else { - $pokemon = $raid['pokemon']; - } - // Name and form. - $name = $pokemon; - $form = 'normal'; - - // Fix for GIRATINA as the actual GIRATINA_ALTERED_FORM is just GIRATINA - if($name == 'GIRATINA' && $form == 'normal') { - $form = 'ALTERED'; - } - } - if($form != 'normal') continue; - // Get ID and form name used internally. - debug_log('Getting dex id and form for pokemon ' . $name . ' with form ' . $form); - $dex_id_form = get_pokemon_id_by_name($name . '-' . $form, true); - $dex_id = explode('-', $dex_id_form, 2)[0]; - $dex_form = explode('-', $dex_id_form, 2)[1]; - - // Make sure we received a valid dex id. - if(!is_numeric($dex_id) || $dex_id == 0) { - info_log('Failed to get a valid pokemon dex id: '. $dex_id .' Continuing with next raid boss...'); - continue; - } - - $shinydata[] = [':dex_id' => $dex_id, ':dex_form' => $dex_form]; + // Raid level and message. + $rl = str_replace('RAID_LEVEL_','', $tier['tier']); + if($rl == "MEGA") $raid_level_id = 6; else $raid_level_id = $rl; + $rl_parts = explode('_', $rl); + if($rl_parts[count($rl_parts)-1] == 'FUTURE') continue; + + // Get raid bosses for each raid level. + foreach($tier['raids'] as $raid) { + if(!isset($raid['pokemon']) || $raid['shiny'] != 'true') continue; + + // Get ID and form name used internally. + [$dex_id, $dex_form] = resolvePokebattlerNameToIds($raid['pokemon']); + + // Make sure we received a valid dex id. + if(!is_numeric($dex_id) || $dex_id == 0) { + info_log('Failed to get a valid pokemon dex id: '. $dex_id .', pokemon: ' . $raid['pokemon'] . '. Continuing with next raid boss...', 'Import shinyinfo:'); + continue; } - $msg .= CR; + $shinydata[] = [':dex_id' => $dex_id, ':dex_form' => $dex_form]; + } } - // Back button. - $keys[] = [ - [ - 'text' => getTranslation('done'), - 'callback_data' => '0:exit:1' - ] - ]; +// Back button. +$keys[][] = button(getTranslation('done'), ['exit', 'd' => '1']); if(count($shinydata) > 0) { - $query = $dbh->prepare("UPDATE pokemon SET shiny = 1 WHERE pokedex_id = :dex_id AND pokemon_form_id = :dex_form"); - foreach($shinydata as $row_data) { - $query->execute($row_data); - } + $query = $dbh->prepare("UPDATE pokemon SET shiny = 1 WHERE pokedex_id = :dex_id AND pokemon_form_id = :dex_form"); + foreach($shinydata as $row_data) { + $query->execute($row_data); + } } $msg .= 'Updated '.count($shinydata).' rows'.CR; @@ -143,6 +71,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/importal.php b/mods/importal.php index fa589151..621c3d15 100644 --- a/mods/importal.php +++ b/mods/importal.php @@ -1,153 +1,126 @@ accessCheck('portal-import'); function escape($value){ - $search = array("\\", "\x00", "\n", "\r", "'", '"', "\x1a"); - $replace = array("\\\\","\\0","\\n", "\\r", "\'", '\"', "\\Z"); + $search = array("\\", "\x00", "\n", "\r", "'", '"', "\x1a"); + $replace = array("\\\\","\\0","\\n", "\\r", "\'", '\"', "\\Z"); - return str_replace($search, $replace, $value); + return str_replace($search, $replace, $value); } // Import allowed? -if($config->PORTAL_IMPORT) { - - // Process message for portal information. - require_once(CORE_BOT_PATH . '/importal.php'); - - // Insert gym. - try { - - global $dbh; - - // Gym name. - $gym_name = $portal; - if(empty($portal)) { - $gym_name = '#' . $update['message']['from']['id']; - } - - // Gym image. - if($config->RAID_PICTURE_STORE_GYM_IMAGES_LOCALLY) { - $no_spaces_gym_name = str_replace(array(' ', '\''), array('_', ''), $gym_name) . '.png'; - $gym_image = download_Portal_Image($portal_image, PORTAL_IMAGES_PATH, $no_spaces_gym_name); - if($gym_image) { - $gym_image = "file://" . $gym_image; - } - } else { - $gym_image = $portal_image; - } - - $gym_name_no_spec = escape($portal); // Convert special characters in gym name - // Build query to check if gym is already in database or not - // First check if gym is found by portal id - $gym_query = 'SELECT id FROM gyms WHERE gym_id = :gym_id LIMIT 1'; - $gym_statement = $dbh->prepare($gym_query); - $gym_statement->execute(['gym_id' => $portal_id]); - if($gym_statement->rowCount() == 1) { - $row = $gym_statement->fetch(); - $update_where_condition = 'gym_id = :gym_id'; - $update_values = ''; - }else { - // If portal id wasn't found, check by gym name - $gym_query_by_name = 'SELECT id FROM gyms WHERE gym_name = :gym_name LIMIT 1'; - $gym_statement_by_name = $dbh->prepare($gym_query_by_name); - $gym_statement_by_name->execute(['gym_name' => $gym_name_no_spec]); - $row = $gym_statement_by_name->fetch(); - $update_where_condition = 'gym_name = :gym_name'; - $update_values = 'gym_id = :gym_id, '; - } - - // Gym already in database or new - if (empty($row['id'])) { - // insert gym in table. - debug_log('Gym not found in database gym list! Inserting gym "' . $gym_name . '" now.'); - $query = ' - INSERT INTO gyms (gym_name, lat, lon, address, show_gym, img_url, gym_id) - VALUES (:gym_name, :lat, :lon, :address, 0, :gym_image, :gym_id) - '; - $msg = getTranslation('gym_added'); - - } else { - // Update gyms table to reflect gym changes. - debug_log('Gym found in database gym list! Updating gym "' . $gym_name . '" now.'); - $query = ' - UPDATE gyms - SET lat = :lat, - lon = :lon, - address = :address, - ' . $update_values . ' - img_url = :gym_image - WHERE ' . $update_where_condition . ' - '; - $msg = getTranslation('gym_updated'); - $gym_id = $row['id']; - } - - // Insert / Update. - $statement = $dbh->prepare($query); - $statement->execute([ - 'gym_name' => $gym_name, - 'lat' => $lat, - 'lon' => $lon, - 'address' => $address, - 'gym_image' => $gym_image, - 'gym_id' => $portal_id - ]); - } catch (PDOException $exception) { - error_log($exception->getMessage()); - $dbh = null; - exit(); - } - - // Get last insert id. - if (empty($row['id'])) { - $gym_id = $dbh->lastInsertId(); - } - - // Gym details. - if($gym_id > 0) { - $gym = get_gym($gym_id); - $msg .= CR . CR . get_gym_details($gym); - } - - // Gym photo. - if($gym_image) { - $msg .= EMOJI_CAMERA . SP . $no_spaces_gym_name; - } - - // Set keys. - $keys = [ - [ - [ - 'text' => getTranslation('delete'), - 'callback_data' => $gym_name[0] . ':gym_delete:' . $gym_id . '-delete' - ], - [ - 'text' => getTranslation('show_gym'), - 'callback_data' => $gym_id . ':gym_edit_details:show-1' - ] - ], - [ - [ - 'text' => getTranslation('done'), - 'callback_data' => '0:exit:1' - ] - ] - ]; +if(!$config->PORTAL_IMPORT) { + $msg = getTranslation('bot_access_denied'); + $keys = []; + send_message(create_chat_object([$update['message']['chat']['id']]), $msg, $keys, ['disable_web_page_preview' => 'true']); + exit; +} + +// Process message for portal information. +require_once(CORE_BOT_PATH . '/importal.php'); +// Gym name. +$gym_name = $portal; +if(empty($portal)) { + $gym_name = '#' . $update['message']['from']['id']; +} + +// Gym image. +if($config->RAID_PICTURE_STORE_GYM_IMAGES_LOCALLY) { + $no_spaces_gym_name = str_replace(array(' ', '\''), array('_', ''), $gym_name) . '.png'; + $gym_image = download_Portal_Image($portal_image, PORTAL_IMAGES_PATH, $no_spaces_gym_name); + if($gym_image) { + $gym_image = "file://" . $gym_image; + } } else { - $msg = getTranslation('bot_access_denied'); - $keys = []; + $gym_image = $portal_image; } -// Send the message. -send_message($update['message']['chat']['id'], $msg, $keys, ['disable_web_page_preview' => 'true']); +$gym_name_no_spec = escape($portal); // Convert special characters in gym name +// Build query to check if gym is already in database or not +// First check if gym is found by portal id +$gym_statement = my_query('SELECT id FROM gyms WHERE gym_id = :gym_id LIMIT 1', ['gym_id' => $portal_id]); +if($gym_statement->rowCount() == 1) { + $row = $gym_statement->fetch(); + $update_where_condition = 'gym_id = :gym_id'; + $update_values = ''; +}else { + // If portal id wasn't found, check by gym name + $gym_statement_by_name = my_query('SELECT id FROM gyms WHERE gym_name = :gym_name LIMIT 1', ['gym_name' => $gym_name_no_spec]); + $row = $gym_statement_by_name->fetch(); + $update_where_condition = 'gym_name = :gym_name'; + $update_values = 'gym_id = :gym_id, '; +} + +// Gym already in database or new +if (empty($row['id'])) { + // insert gym in table. + debug_log('Gym not found in database gym list! Inserting gym "' . $gym_name . '" now.'); + $query = ' + INSERT INTO gyms (gym_name, lat, lon, address, show_gym, img_url, gym_id) + VALUES (:gym_name, :lat, :lon, :address, 0, :gym_image, :gym_id) + '; + $msg = getTranslation('gym_added'); -?> +} else { + // Update gyms table to reflect gym changes. + debug_log('Gym found in database gym list! Updating gym "' . $gym_name . '" now.'); + $query = ' + UPDATE gyms + SET lat = :lat, + lon = :lon, + gym_name = :gym_name, + address = :address, + ' . $update_values . ' + img_url = :gym_image + WHERE ' . $update_where_condition . ' + '; + $msg = getTranslation('gym_updated'); + $gym_id = $row['id']; +} + +// Insert / Update. +$statement = my_query( + $query,[ + 'gym_name' => $gym_name_no_spec, + 'lat' => $lat, + 'lon' => $lon, + 'address' => $address, + 'gym_image' => $gym_image, + 'gym_id' => $portal_id +]); + + +// Get last insert id. +if (empty($row['id'])) { + $gym_id = $dbh->lastInsertId(); +} + +// Gym details. +if($gym_id > 0) { + $gym = get_gym($gym_id); + $msg .= CR . CR . get_gym_details($gym); +} + +// Gym photo. +if($config->RAID_PICTURE_STORE_GYM_IMAGES_LOCALLY && $gym_image) { + $msg .= EMOJI_CAMERA . SP . $no_spaces_gym_name; +} + +// Set keys. +$keys[][] = button(getTranslation('delete'), ['gym_delete', 'fl' => $gym_name[0], 'g' => $gym_id, 'c' => 0]); +$keys[][] = button(getTranslation('show_gym'), ['gym_edit_details', 'g' => $gym_id, 'a' => 'show', 'v' => 1]); +$keys[][] = button(getTranslation('done'), ['exit', 'd' => '1']); + +// Send the message. +send_message(create_chat_object([$update['message']['chat']['id']]), $msg, $keys, ['disable_web_page_preview' => 'true']); diff --git a/mods/list_by_gym.php b/mods/list_by_gym.php deleted file mode 100644 index cac040a2..00000000 --- a/mods/list_by_gym.php +++ /dev/null @@ -1,62 +0,0 @@ - 1) ? $args[1] : false; - -// Back key id, action and arg -$back_id = 'n'; -$back_action = 'list_by_gym_letter'; -$back_arg = 0; - -// Get the keys. -$keys = raid_edit_gym_keys($first, $gymarea_id, 'list_raid'); - -// No keys found. -if (!$keys) { - // Create the keys. - $keys = [ - [ - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ] - ]; -} else { - // Add navigation keys. - $nav_keys = []; - $nav_keys[] = universal_inner_key($nav_keys, $back_id, $back_action, $back_arg, getTranslation('back')); - $nav_keys[] = universal_inner_key($nav_keys, '0', 'exit', '0', getTranslation('abort')); - $nav_keys = inline_key_array($nav_keys, 2); - // Merge keys. - $keys = array_merge($keys, $nav_keys); -} - -// Build callback message string. -$callback_response = getTranslation('here_we_go'); - -// Telegram JSON array. -$tg_json = array(); - -// Answer callback. -$tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_response, true); - -// Edit the message. -$tg_json[] = edit_message($update, getTranslation('select_gym_name'), $keys, false, true); - -// Telegram multicurl request. -curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/list_by_gym_letter.php b/mods/list_by_gym_letter.php deleted file mode 100644 index 24a703ad..00000000 --- a/mods/list_by_gym_letter.php +++ /dev/null @@ -1,72 +0,0 @@ - getTranslation('not_supported'), - 'callback_data' => '0:exit:0' - ] - ] - ]; -} - -// Build callback message string. -$callback_response = getTranslation('select_gym'); - -// Telegram JSON array. -$tg_json = array(); - -// Answer callback. -$tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_response, true); - -// Edit the message. -$msg = '' . getTranslation('list_all_active_raids') . '' . CR; -if($config->ENABLE_GYM_AREAS) { - if($keys_and_gymarea['gymarea_name'] == '') { - $msg .= '' . getTranslation('select_gym_area') . '' . CR; - }else { - if($keys_and_gymarea['letters']) { - $msg .= '' . getTranslation('select_gym_first_letter_or_gym_area') . '' . CR; - }else { - $msg .= '' . getTranslation('select_gym_name_or_gym_area') . '' . CR; - } - } -}elseif($keys_and_gymarea['letters']) { - $msg .= '' . getTranslation('select_gym_first_letter') . '' . CR; -}else { - $msg .= '' . getTranslation('select_gym_name') . '' . CR; -} -$msg.= (($keys_and_gymarea['gymarea_name'] != '') ? CR . CR . getTranslation('current_gymarea') . ': ' . $keys_and_gymarea['gymarea_name'] : ''); -$tg_json[] = edit_message($update, $msg, $keys, false, true); - -// Telegram multicurl request. -curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/list_raid.php b/mods/list_raid.php index e675a827..0dcf85ba 100644 --- a/mods/list_raid.php +++ b/mods/list_raid.php @@ -1,106 +1,97 @@ accessCheck('list'); // Get gym ID. -$gym_id = $data['arg']; -$raid_id = $data['id']; +$gym_id = $data['g'] ?? 0; +$raid_id = $data['r'] ?? 0; // Get raid details. if($raid_id != 0) { - $sql_condition = 'AND raids.id = ? LIMIT 1'; - $binds = [$raid_id]; + $sql_condition = 'AND raids.id = ? LIMIT 1'; + $binds = [$raid_id]; }else { - $sql_condition = 'AND gyms.id = ?'; - $binds = [$gym_id]; + $eventQuery = 'event IS NULL'; + if($botUser->accessCheck('ex-raids', true)) { + if($botUser->accessCheck('event-raids', true)) + $eventQuery = ''; + else + $eventQuery .= ' OR event = ' . EVENT_ID_EX; + }elseif($botUser->accessCheck('event-raids', true)) { + $eventQuery = 'event != ' . EVENT_ID_EX .' OR event IS NULL'; + } + $eventQuery = ($eventQuery == '') ? ' ' : ' AND ('.$eventQuery.') '; + $sql_condition = 'AND gyms.id = ? ' . $eventQuery; + $binds = [$gym_id]; } -$rs = my_query( - " - SELECT raids.id - FROM raids - LEFT JOIN gyms - ON raids.gym_id = gyms.id - WHERE end_time > UTC_TIMESTAMP() - INTERVAL 10 MINUTE - {$sql_condition} - ", - $binds +$rs = my_query(' + SELECT raids.id + FROM raids + LEFT JOIN gyms + ON raids.gym_id = gyms.id + WHERE end_time > UTC_TIMESTAMP() + ' . $sql_condition + ,$binds ); if($rs->rowcount() == 1) { - // Get the row. - $raid_fetch = $rs->fetch(); - $raid = get_raid($raid_fetch['id']); + // Get the row. + $raid_fetch = $rs->fetch(); + $raid = get_raid($raid_fetch['id']); - debug_log($raid); + debug_log($raid); - // Create keys array. - $keys = [ - [ - [ - 'text' => getTranslation('expand'), - 'callback_data' => $raid['id'] . ':vote_refresh:0', - ] - ], - [ - [ - 'text' => getTranslation('update_pokemon'), - 'callback_data' => $raid['id'] . ':raid_edit_poke:' . $raid['level'], - ] - ], - [ - [ - 'text' => getTranslation('delete'), - 'callback_data' => $raid['id'] . ':raids_delete:0' - ] - ] - ]; + // Create keys array. + $keys = []; + // Probably unused feature. Will fix if someone needs this + // $keys[][] = button(getTranslation('expand'), ['vote_refresh', 'r' => $raid['id']]); + if($botUser->raidaccessCheck($raid['id'], 'pokemon', true)) { + $keys[][] = button(getTranslation('update_pokemon'), ['raid_edit_poke', 'r' => $raid['id'], 'rl' => $raid['level']]); + } + if($botUser->raidaccessCheck($raid['id'], 'delete', true)) { + $keys[][] = button(getTranslation('delete'), ['raids_delete', 'r' => $raid['id']]); + } - // Add keys to share. - debug_log($raid, 'raw raid data for share: '); - $keys_share = share_keys($raid['id'], 'raid_share', $update, '', '', false, $raid['level']); - if(is_array($keys_share)) { - $keys = array_merge($keys, $keys_share); - } else { - debug_log('There are no groups to share to, is SHARE_CHATS set?'); - } - // Exit key - $empty_exit_key = []; - $key_exit = universal_key($empty_exit_key, '0', 'exit', '1', getTranslation('done')); - $keys = array_merge($keys, $key_exit); + // Add keys to share. + debug_log($raid, 'raw raid data for share: '); + $keys_share = share_keys($raid['id'], 'raid_share', $update, $raid['level']); + if(!empty($keys_share)) { + $keys = array_merge($keys, $keys_share); + } else { + debug_log('There are no groups to share to, is SHARE_CHATS set?'); + } + // Exit key + $keys[][] = button(getTranslation('done'), ['exit', 'd' => 1]); - // Get message. - $msg = show_raid_poll_small($raid); + // Get message. + $msg = show_raid_poll_small($raid); }else { - $msg = getTranslation('list_all_active_raids').':'. CR; - $keys = []; - $i = 1; - while($raid_fetch = $rs->fetch()) { - $raid = get_raid($raid_fetch['id']); - $raid_pokemon_name = get_local_pokemon_name($raid['pokemon'], $raid['pokemon_form']); - $msg .= '' . $i .'. ' . $raid_pokemon_name . '' . CR; - if(!empty($raid['event_name'])) $msg .= $raid['event_name'] . CR; - $msg .= get_raid_times($raid,false, true) . CR . CR; - $keys[] = [ - [ - 'text' => $i . '. ' . $raid_pokemon_name, - 'callback_data' => $raid['id'] . ':list_raid:0' - ] - ]; - $i++; - } - $keys[] = [ - [ - 'text' => getTranslation('back'), - 'callback_data' => '0:list_by_gym:' . $raid['gym_name'][0] - ] - ]; + $msg = getTranslation('list_all_active_raids').':'. CR; + $keys = []; + $i = 1; + while($raid_fetch = $rs->fetch()) { + $raid = get_raid($raid_fetch['id']); + $raid_pokemon_name = get_local_pokemon_name($raid['pokemon'], $raid['pokemon_form']); + $msg .= '' . $i .'. ' . $raid_pokemon_name . '' . CR; + if(!empty($raid['event_name'])) $msg .= $raid['event_name'] . CR; + $msg .= get_raid_times($raid, $botUser->userLanguage, true) . CR . CR; + $keys[][] = button($i . '. ' . $raid_pokemon_name,['list_raid', 'r' => $raid['id']]); + $i++; + } + $backData = $data; + $backData[0] = 'gymMenu'; + $backData['stage'] = 2; + $backData['a'] = 'list'; + $keys[][] = button(getTranslation('back'), $backData); } // Build callback message string. @@ -117,6 +108,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/list_remote_gyms.php b/mods/list_remote_gyms.php index 24326b5d..1837f41d 100644 --- a/mods/list_remote_gyms.php +++ b/mods/list_remote_gyms.php @@ -6,30 +6,22 @@ //debug_log($update); //debug_log($data); -// Back key id, action and arg -$back_id = 0; -$back_action = 'list_by_gym_letter'; -$back_arg = 0; - $user_id = $update['callback_query']['from']['id']; // Get the keys. +$keys = []; $query_remote = my_query('SELECT raids.id, gyms.gym_name, raids.start_time, raids.end_time FROM gyms LEFT JOIN raids on raids.gym_id = gyms.id WHERE raids.end_time > (UTC_TIMESTAMP() - INTERVAL 10 MINUTE) AND temporary_gym = 1'); while($gym = $query_remote->fetch()) { - $keys[][] = [ - 'text' => $gym['gym_name'], - 'callback_data' => $gym['id'] . ':list_raid:' - ]; -} + $keys[][] = button($gym['gym_name'], ['list_raid', 'r' => $gym['id']]); +} // Add navigation keys. $nav_keys = []; -$nav_keys[] = universal_inner_key($nav_keys, $back_id, $back_action, $back_arg, getTranslation('back')); -$nav_keys[] = universal_inner_key($nav_keys, '0', 'exit', '0', getTranslation('abort')); +$nav_keys[] = button(getTranslation('back'), ['gymMenu', 'a' => 'list']); +$nav_keys[] = button(getTranslation('abort'), 'exit'); $nav_keys = inline_key_array($nav_keys, 2); // Merge keys. $keys = array_merge($keys, $nav_keys); - // Build callback message string. $callback_response = getTranslation('here_we_go'); @@ -44,6 +36,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/listall.php b/mods/listall.php deleted file mode 100644 index 480eef17..00000000 --- a/mods/listall.php +++ /dev/null @@ -1,67 +0,0 @@ -' . getTranslation('list_all_active_raids') . '' . CR; -if($config->ENABLE_GYM_AREAS) { - if($keys_and_gymarea['gymarea_name'] == '') { - $msg .= '' . getTranslation('select_gym_area') . '' . CR; - }elseif($config->DEFAULT_GYM_AREA !== false) { - if($keys_and_gymarea['letters']) { - $msg .= '' . getTranslation('select_gym_first_letter_or_gym_area') . '' . CR; - }else { - $msg .= '' . getTranslation('select_gym_name_or_gym_area') . '' . CR; - } - }else { - if($keys_and_gymarea['letters']) { - $msg .= '' . getTranslation('select_gym_first_letter') . '' . CR; - }else { - $msg .= '' . getTranslation('select_gym_name') . '' . CR; - } - } -}else { - if($keys_and_gymarea['letters']) { - $msg .= '' . getTranslation('select_gym_first_letter') . '' . CR; - }else { - $msg .= '' . getTranslation('select_gym_name') . '' . CR; - } -} -$msg.= (($keys_and_gymarea['gymarea_name'] != '') ? CR . getTranslation('current_gymarea') . ': ' . $keys_and_gymarea['gymarea_name'] : ''); -$tg_json[] = edit_message($update, $msg, $keys, false, true); - -// Telegram multicurl request. -curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/overview_delete.php b/mods/overview_delete.php index 9c600bfe..ed20c4a8 100644 --- a/mods/overview_delete.php +++ b/mods/overview_delete.php @@ -1,115 +1,102 @@ accessCheck('overview'); // Telegram JSON array. $tg_json = array(); // Get all or specific overview -if ($chat_id == 0) { - $request_overviews = my_query( - " - SELECT * - FROM overview - " - ); - - // Count results. - $count = 0; - - while ($rowOverviews = $request_overviews->fetch()) { - // Counter++ - $count = $count + 1; - - // Get info about chat for title. - debug_log('Getting chat object for chat_id: ' . $rowOverviews['chat_id']); - $chat_obj = get_chat($rowOverviews['chat_id']); - $chat_title = ''; - - // Set title. - if ($chat_obj['ok'] == 'true') { - $chat_title = $chat_obj['result']['title']; - debug_log('Title of the chat: ' . $chat_obj['result']['title']); - } - - // Build message string. - $msg = '' . getTranslation('delete_raid_overview_for_chat') . ' ' . $chat_title . '?'; - - // Set keys - Delete button. - $keys = [ - [ - [ - 'text' => getTranslation('yes'), - 'callback_data' => '0:overview_delete:' . $rowOverviews['chat_id'] - ], - [ - 'text' => getTranslation('no'), - 'callback_data' => '0:overview_delete:1' - ] - ] - ]; - - // Send the message, but disable the web preview! - $tg_json[] = send_message($update['callback_query']['message']['chat']['id'], $msg, $keys, false, true); +if ($action == 0) { + $request_overviews = my_query(' + SELECT * + FROM overview + '); + + while ($rowOverviews = $request_overviews->fetch()) { + + // Get info about chat for title. + debug_log('Getting chat object for chat_id: ' . $rowOverviews['chat_id']); + $chat_obj = get_config_chat_by_chat_and_thread_id($rowOverviews['chat_id'], $rowOverviews['thread_id']); + if(!isset($chat_obj['title'])) { + $chat_info = get_chat($rowOverviews['chat_id']); + $chat_title = ''; + + // Set title. + if ($chat_info['ok'] == 'true') { + $chat_title = $chat_info['result']['title']; + debug_log('Title of the chat: ' . $chat_info['result']['title']); + } + }else { + $chat_title = $chat_obj['title']; } - // Set message. - if($count == 0) { - $callback_msg = '' . getTranslation('no_overviews_found') . ''; - } else { - $callback_msg = '' . getTranslation('list_all_overviews') . ':'; - } -} else if ($chat_id == 1) { - // Write to log. - debug_log('Deletion of the raid overview was canceled!'); - - // Set message. - $callback_msg = '' . getTranslation('overview_deletion_was_canceled') . ''; + // Build message string. + $msg = '' . getTranslation('delete_raid_overview_for_chat') . ' ' . $chat_title . '?'; + + // Set keys - Delete button. + $keys[0][0] = button(getTranslation('yes'), ['overview_delete', 'o' => $rowOverviews['id'], 'a' => 3]); + $keys[0][1] = button(getTranslation('no'), ['overview_delete', 'a' => 1]); + + // Send the message, but disable the web preview! + $tg_json[] = send_message(create_chat_object([$update['callback_query']['message']['chat']['id']]), $msg, $keys, false, true); + } + + // Set message. + if($request_overviews->rowCount() == 0) { + $callback_msg = '' . getTranslation('no_overviews_found') . ''; + } else { + $callback_msg = '' . getTranslation('list_all_overviews') . ':'; + } +} else if ($action == 1) { + // Write to log. + debug_log('Deletion of the raid overview was canceled!'); + + // Set message. + $callback_msg = '' . getTranslation('overview_deletion_was_canceled') . ''; } else { - // Write to log. - debug_log('Triggering deletion of overview for Chat_ID ' . $chat_id); - - // Get chat and message ids for overview. - $request_overviews = my_query( - " - SELECT * - FROM overview - WHERE chat_id = '{$chat_id}' - " - ); - - $overview = $request_overviews->fetch(); - - // Delete overview - $chat_id = $overview['chat_id']; - $message_id = $overview['message_id']; - - // Delete telegram message. - debug_log('Deleting overview telegram message ' . $message_id . ' from chat ' . $chat_id); - delete_message($chat_id, $message_id); - - // Delete overview from database. - debug_log('Deleting overview information from database for Chat_ID: ' . $chat_id); - $rs = my_query( - " - DELETE FROM overview - WHERE chat_id = '{$chat_id}' - " - ); - - // Set message. - $callback_msg = '' . getTranslation('overview_successfully_deleted') . ''; + + // Get chat and message ids for overview. + $request_overviews = my_query(' + SELECT * + FROM overview + WHERE id = ? + ', [$overview_id] + ); + + $overview = $request_overviews->fetch(); + + // Delete overview + $chat_id = $overview['chat_id']; + $message_id = $overview['message_id']; + // Write to log. + debug_log('Triggering deletion of overview for Chat_ID ' . $chat_id); + + // Delete telegram message. + debug_log('Deleting overview telegram message ' . $message_id . ' from chat ' . $chat_id); + delete_message($chat_id, $message_id); + + // Delete overview from database. + debug_log('Deleting overview information from database for Chat_ID: ' . $chat_id . ', thread_id: ' . $overview['thread_id']); + $rs = my_query(' + DELETE FROM overview + WHERE id = ? + ', [$overview_id] + ); + + // Set message. + $callback_msg = '' . getTranslation('overview_successfully_deleted') . ''; } // Set keys. @@ -126,7 +113,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -$dbh = null; -exit(); diff --git a/mods/overview_refresh.php b/mods/overview_refresh.php index 2428bba1..9fc18d8e 100644 --- a/mods/overview_refresh.php +++ b/mods/overview_refresh.php @@ -1,6 +1,8 @@ fetchAll(); @@ -31,71 +30,67 @@ $active_raids = []; $tg_json = []; foreach($overviews as $overview_row) { - $request_raids = my_query(' - SELECT raids.pokemon, raids.pokemon_form, raids.spawn, raids.level, raids.id, raids.start_time, raids.end_time, raids.gym_id, - MAX(cleanup.message_id) as message_id, - events.name as event_name, - gyms.lat, gyms.lon, gyms.address, gyms.gym_name, gyms.ex_gym, - TIME_FORMAT(TIMEDIFF(end_time, UTC_TIMESTAMP()) + INTERVAL 1 MINUTE, \'%k:%i\') AS t_left - FROM cleanup - LEFT JOIN raids - ON raids.id = cleanup.raid_id - LEFT JOIN gyms - ON raids.gym_id = gyms.id - LEFT JOIN events - ON events.id = raids.event - WHERE cleanup.chat_id = \'' . $overview_row['chat_id'] . '\' - AND raids.end_time>UTC_TIMESTAMP() - GROUP BY raids.id, raids.pokemon, raids.pokemon_form, raids.start_time, raids.end_time, raids.gym_id, gyms.lat, gyms.lon, gyms.address, gyms.gym_name, gyms.ex_gym, events.name - ORDER BY raids.end_time ASC, gyms.gym_name - '); - // Write active raids to array - $active_raids = $request_raids->fetchAll(); - debug_log('Active raids:'); - debug_log($active_raids); + $request_raids = my_query(' + SELECT raids.pokemon, raids.pokemon_form, raids.spawn, raids.level, raids.id, raids.start_time, raids.end_time, raids.gym_id, + MAX(cleanup.message_id) as message_id, + events.name as event_name, + gyms.lat, gyms.lon, gyms.address, gyms.gym_name, gyms.ex_gym, + TIME_FORMAT(TIMEDIFF(end_time, UTC_TIMESTAMP()) + INTERVAL 1 MINUTE, \'%k:%i\') AS t_left + FROM cleanup + LEFT JOIN raids + ON raids.id = cleanup.raid_id + LEFT JOIN gyms + ON raids.gym_id = gyms.id + LEFT JOIN events + ON events.id = raids.event + WHERE cleanup.chat_id = ? + AND raids.end_time>UTC_TIMESTAMP() + GROUP BY raids.id, raids.pokemon, raids.pokemon_form, raids.start_time, raids.end_time, raids.gym_id, gyms.lat, gyms.lon, gyms.address, gyms.gym_name, gyms.ex_gym, events.name + ORDER BY raids.end_time ASC, gyms.gym_name + ', [$overview_row['chat_id']] + ); + // Write active raids to array + $active_raids = $request_raids->fetchAll(); + debug_log('Active raids:'); + debug_log($active_raids); - if($overview_row['update_needed'] == 1) { - $chat_title_username = get_chat_title_username($overview_row['chat_id']); - $chat_title = $chat_title_username[0]; - $chat_username = $chat_title_username[1]; - my_query(' - UPDATE overview - SET chat_title = \''.$chat_title.'\', - chat_username = \''.$chat_username.'\', - updated = DATE(NOW()) - WHERE chat_id = \''.$overview_row['chat_id'].'\' - '); - }else { - $chat_title = $overview_row['chat_title']; - $chat_username = $overview_row['chat_username']; - } - $overview_message = get_overview($active_raids, $chat_title, $chat_username); - // Triggered from user or cronjob? - if (!empty($update['callback_query']['id'])) { - // Answer the callback. - answerCallbackQuery($update['callback_query']['id'], 'OK'); - $message_id = $update['callback_query']['message']['message_id']; - $chat_id = $update['callback_query']['from']['id']; - $keys[] = [ - [ - 'text' => EMOJI_REFRESH, - 'callback_data' => '0:overview_refresh:' . $overview_row['chat_id'] - ], - [ - 'text' => getTranslation('done'), - 'callback_data' => '0:exit:1' - ] - ]; - }else { - $message_id = $overview_row['message_id']; - $chat_id = $overview_row['chat_id']; - $keys = []; + if($overview_row['update_needed'] == 1) { + [$chat_title, $chat_username] = get_chat_title_username($overview_row['chat_id']); + if($chat_title != '' && $chat_username != '') { + my_query(' + UPDATE overview + SET chat_title = ?, + chat_username = ?, + updated = DATE(NOW()) + WHERE chat_id = ? + ',[$chat_title, $chat_username, $overview_row['chat_id']] + ); } + }else { + $chat_title = $overview_row['chat_title']; + $chat_username = $overview_row['chat_username']; + } + $overview_message = get_overview($active_raids, $chat_title, $chat_username); + // Triggered from user or cronjob? + if (!empty($update['callback_query']['id'])) { + // Answer the callback. + answerCallbackQuery($update['callback_query']['id'], 'OK'); + $message_id = $update['callback_query']['message']['message_id']; + $chat_id = $update['callback_query']['from']['id']; + $keys[] = [ + button(EMOJI_REFRESH, ['overview_refresh', 'c' => $overview_row['chat_id']]), + button(getTranslation('done'), ['exit', 'd' => '1']) + ]; + }else { + $message_id = $overview_row['message_id']; + $chat_id = $overview_row['chat_id']; + $keys = []; + } - $tg_json[] = editMessageText($message_id, $overview_message, $keys, $chat_id, ['disable_web_page_preview' => 'true'], true); + $tg_json[] = editMessageText($message_id, $overview_message, $keys, $chat_id, ['disable_web_page_preview' => 'true'], true); } // Telegram multicurl request. curl_json_multi_request($tg_json); $dbh=null; -exit; \ No newline at end of file +exit; diff --git a/mods/overview_share.php b/mods/overview_share.php index 2e4aedf6..6bc7d2ee 100644 --- a/mods/overview_share.php +++ b/mods/overview_share.php @@ -1,123 +1,107 @@ accessCheck('overview'); // Get chat ID from data -$chat_id = 0; -$chat_id = $data['arg']; - -// Get all or specific overview -$query_chat = ""; -if ($chat_id != 0) { - $query_chat = "AND chat_id = '{$chat_id}'"; -} -// Get active raids. -$request_active_raids = my_query( - " - SELECT - cleanup.chat_id, cleanup.message_id, - raids.*, - gyms.lat, gyms.lon, gyms.address, gyms.gym_name, gyms.ex_gym, - TIME_FORMAT(TIMEDIFF(end_time, UTC_TIMESTAMP()) + INTERVAL 1 MINUTE, '%k:%i') AS t_left - FROM cleanup - LEFT JOIN raids - ON raids.id = cleanup.raid_id - LEFT JOIN gyms - ON raids.gym_id = gyms.id - WHERE raids.end_time>UTC_TIMESTAMP() - {$query_chat} - ORDER BY cleanup.chat_id, raids.end_time ASC, gyms.gym_name - " -); -// Collect results in an array -$active_raids = $request_active_raids->fetchAll(PDO::FETCH_GROUP); +$chat_id = $data['c'] ?? NULL; $tg_json = []; // Share an overview -if($chat_id != 0) { - $chat_title_username = get_chat_title_username($chat_id); - $overview_message = get_overview($active_raids[$chat_id], $chat_title_username[1], $chat_title_username[0]); - // Shared overview - $keys = []; +if($chat_id != NULL) { + $query_chat = ''; + $chatObj = get_config_chat_by_short_id($chat_id); + $query_chat = 'AND chat_id = ?'; + $binds[] = $chatObj['id']; + if(isset($chatObj['thread'])) { + $query_chat .= ' AND thread_id = ?'; + $binds[] = $chatObj['thread']; + } + // Get active raids. + $request_active_raids = my_query(' + SELECT + cleanup.chat_id, cleanup.thread_id, cleanup.message_id, + raids.*, + gyms.lat, gyms.lon, gyms.address, gyms.gym_name, gyms.ex_gym, + TIME_FORMAT(TIMEDIFF(end_time, UTC_TIMESTAMP()) + INTERVAL 1 MINUTE, \'%k:%i\') AS t_left + FROM cleanup + LEFT JOIN raids + ON raids.id = cleanup.raid_id + LEFT JOIN gyms + ON raids.gym_id = gyms.id + WHERE raids.end_time>UTC_TIMESTAMP() + ' . $query_chat . ' + ORDER BY cleanup.chat_id, raids.end_time ASC, gyms.gym_name + ', $binds); + // Collect results in an array + $active_raids = $request_active_raids->fetchAll(); + [$chat_title, $chat_username] = get_chat_title_username($chatObj['id']); + $title = $chatObj['title'] ?? $chat_title; + $overview_message = get_overview($active_raids, $title, $chat_username); + // Shared overview + $keys = []; - // Set callback message string. - $msg_callback = getTranslation('successfully_shared'); + // Set callback message string. + $msg_callback = getTranslation('successfully_shared'); - // Answer the callback. - $tg_json[] = answerCallbackQuery($update['callback_query']['id'], $msg_callback, true); + // Answer the callback. + $tg_json[] = answerCallbackQuery($update['callback_query']['id'], $msg_callback, true); - // Edit the message, but disable the web preview! - $tg_json[] = edit_message($update, $msg_callback, $keys, ['disable_web_page_preview' => 'true'], true); + // Edit the message, but disable the web preview! + $tg_json[] = edit_message($update, $msg_callback, $keys, ['disable_web_page_preview' => 'true'], true); - // Send the message, but disable the web preview! - $tg_json[] = send_message($chat_id, $overview_message, $keys, ['disable_web_page_preview' => 'true'], true, 'overview'); -}else { - // List all overviews to user - foreach( array_keys($active_raids) as $chat_id ) { - // Make sure it's not already shared - $rs = my_query( - " - SELECT chat_id, message_id, chat_title, chat_username - FROM overview - WHERE chat_id = '{$chat_id}' - LIMIT 1 - " - ); - // Already shared - if($rs->rowCount() > 0 ) { - $keys = [ - [ - [ - 'text' => EMOJI_REFRESH, - 'callback_data' => '0:overview_refresh:' . $chat_id - ], - [ - 'text' => getTranslation('done'), - 'callback_data' => '0:exit:1' - ] - ] - ]; - $res = $rs->fetch(); - $chat_title = $res['chat_title']; - $chat_username = $res['chat_username']; - }else { - $chat_title_username = get_chat_title_username($chat_id); - $chat_title = $chat_title_username[0]; - $chat_username = $chat_title_username[1]; - $keys = [ - [ - [ - 'text' => getTranslation('share_with') . ' ' . $chat_title, - 'callback_data' => '0:overview_share:' . $chat_id - ] - ] - ]; - } - $overview_message = get_overview($active_raids[$chat_id], $chat_title, $chat_username); - // Send the message, but disable the web preview! - $tg_json[] = send_message($update['callback_query']['message']['chat']['id'], $overview_message, $keys, ['disable_web_page_preview' => 'true'], true); - } - // Set the callback message and keys - $callback_keys = []; - $callback_msg = '' . getTranslation('list_all_overviews') . ':'; + // Send the message, but disable the web preview! + $tg_json[] = send_message($chatObj, $overview_message, $keys, ['disable_web_page_preview' => 'true'], true, 'overview'); + // Telegram multicurl request. + curl_json_multi_request($tg_json); - // Answer the callback. - $tg_json[] = answerCallbackQuery($update['callback_query']['id'], 'OK', true); + exit; +} +$keys = []; +// List all overviews to user +foreach( list_config_chats_by_short_id() as $short_id => $chat ) { + $binds = [$chat['id']]; + $threadQuery = ' = ?'; + if(!isset($chat['thread']) or $chat['thread'] == 0) { + $threadQuery = 'IS NULL'; + }else { + $binds[] = $chat['thread']; + } + // Make sure it's not already shared + $rs = my_query(' + SELECT chat_id, thread_id, message_id, chat_title, chat_username + FROM overview + WHERE chat_id = ? + AND thread_id ' . $threadQuery . ' + LIMIT 1 + ', $binds + ); + // Already shared + if($rs->rowCount() > 0 ) continue; - // Edit the message. - $tg_json[] = edit_message($update, $callback_msg, $callback_keys, false, true); + [$chat_title, $chat_username] = get_chat_title_username($chat['id']); + $title = $chat['title'] ?? $chat_title; + $keys[][] = button(getTranslation('share_with') . ' ' . $title, ['overview_share', 'c' => $short_id]); } +// Set the callback message and keys +$msg = '' . getTranslation('list_all_overviews') . ':'; +$keys[][] = button(getTranslation('abort'), ['exit', 'd' => '0']); + +// Answer the callback. +$tg_json[] = answerCallbackQuery($update['callback_query']['id'], 'OK', true); + +// Edit the message. +$tg_json[] = edit_message($update, $msg, $keys, false, true); // Telegram multicurl request. curl_json_multi_request($tg_json); - -$dbh = null; -exit; \ No newline at end of file diff --git a/mods/pogoinfo.php b/mods/pogoinfo.php index 47f1c840..4ee997cb 100644 --- a/mods/pogoinfo.php +++ b/mods/pogoinfo.php @@ -1,344 +1,228 @@ accessCheck('pokedex'); // Levels available for import $levels = array('6', '5', '3', '1'); - -// Get raid levels -$id = $data['id']; - -// Exclude pokemon -$arg = $data['arg']; +$id = $data['rl'] ?? 0; +$action = $data['a'] ?? ''; // Raid level selection if($id == 0) { - // Set message. - $msg = '' . getTranslation('import') . SP . '(ccev pogoinfo)' . '' . CR . CR; - $msg .= '' . getTranslation('select_raid_level') . ':'; - - // Init empty keys array. - $keys = []; - - // All raid level keys. - $keys[][] = array( - 'text' => getTranslation('pokedex_all_raid_level'), - 'callback_data' => RAID_LEVEL_ALL . ':pogoinfo:ex#0,0,0' - ); - - // Add key for each raid level - foreach($levels as $l) { - $keys[][] = array( - 'text' => getTranslation($l . 'stars'), - 'callback_data' => $l . ':pogoinfo:ex#0,0,0' - ); - } - $keys[][] = array( - 'text' => getTranslation('1stars') . ' & ' . getTranslation('3stars'), - 'callback_data' => '3,1:pogoinfo:ex#0,0,0' - ); - - // Add back and abort buttons - $keys[] = [ - [ - 'text' => getTranslation('back'), - 'callback_data' => '0:pokedex_import:0' - ], - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ]; - -} else if($id > 0) { - // Set message and init message to exclude raid bosses. - $msg = '' . getTranslation('import') . SP . '(ccev pogoinfo)' . '' . CR . CR; - $ex_msg = ''; - - // Get pogoinfo data. - debug_log('Getting raid bosses from pogoinfo repository now...'); - $link = 'https://raw.githubusercontent.com/ccev/pogoinfo/v2/active/raids.json'; - $data = curl_get_contents($link); - $data = json_decode($data,true); - - // All raid levels? - if($id == RAID_LEVEL_ALL) { - $get_levels = $levels; - $clear = "'6','5','3','1'"; - } else { - $get_levels = explode(",", $id); - $clear = $id; - } + // Set message. + $msg = '' . getTranslation('import') . SP . '(ccev pogoinfo)' . '' . CR . CR; + $msg .= '' . getTranslation('select_raid_level') . ':'; + + // Init empty keys array. + $keys = []; + + // All raid level keys. + $keys[][] = button(getTranslation('pokedex_all_raid_level'), ['pogoinfo', 'rl' => 'all']); + + // Add key for each raid level + foreach($levels as $l) { + $keys[][] = button(getTranslation($l . 'stars'), ['pogoinfo', 'rl' => $l]); + } + $keys[][] = button(getTranslation('1stars') . ' & ' . getTranslation('3stars'), ['pogoinfo', 'rl' => '1,3']); + + // Add back and abort buttons + $keys[] = [ + button(getTranslation('back'), 'pokedex_import'), + button(getTranslation('abort'), 'exit') + ]; + + // Callback message string. + $callback_response = 'OK'; + + // Telegram JSON array. + $tg_json = array(); + + // Answer callback. + $tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_response, true); + + // Edit message. + $tg_json[] = edit_message($update, $msg, $keys, false, true); + + // Telegram multicurl request. + curl_json_multi_request($tg_json); + exit; +} +// Set message and init message to exclude raid bosses. +$msg = '' . getTranslation('import') . SP . '(ccev pogoinfo)' . '' . CR . CR; +$ex_msg = ''; + +// Get pogoinfo data. +debug_log('Getting raid bosses from pogoinfo repository now...'); +$link = 'https://raw.githubusercontent.com/ccev/pogoinfo/v2/active/raids.json'; +$raiddata = curl_get_contents($link); +$raiddata = json_decode($raiddata,true); + +// All raid levels? +if($id == 'all') { + $get_levels = $levels; + $clear = "'6','5','3','1'"; +} else { + $get_levels = explode(",", $id); + $clear = $id; +} + +// New request +$exclusions = isset($data['e']) ? explode('#', $data['e']) : []; +if(!empty($exclusions[0])) { + debug_log('Excluded raid bosses: ' . implode(', ', $exclusions)); +} - // Prefix for exclusion. - $prefix = 'ex#'; - - // New request - if($arg == 'ex#0,0,0') { - $poke1 = 0; - $poke2 = 0; - $poke3 = 0; - - // Get raid bosses to exclude. - } else if(strpos($arg, 'ex#') === 0 || strpos($arg, 'save#') === 0) { - $poke_ids = explode('#', $arg); - $poke_ids = explode(',', $poke_ids[1]); - $poke1 = $poke_ids[0]; - $poke2 = $poke_ids[1]; - $poke3 = $poke_ids[2]; - debug_log('Excluded raid boss #1: ' . $poke1); - debug_log('Excluded raid boss #2: ' . $poke2); - debug_log('Excluded raid boss #3: ' . $poke3); +// Clear old raid bosses. +if($action == 's') { + require_once(LOGIC_PATH . '/disable_raid_level.php'); + debug_log('Disabling old raid bosses for levels: '. $clear); + disable_raid_level($clear); +} + +// Raid tier array +debug_log('Processing the following raid levels:'); +debug_log($get_levels); + +// Process raid tier(s) +debug_log('Processing received ccev pogoinfo raid bosses for each raid level'); +foreach($raiddata as $tier => $tier_pokemon) { + // Process raid level? + if(!in_array($tier,$get_levels)) { + continue; + } + // Raid level and message. + $msg .= '' . getTranslation('pokedex_raid_level') . SP . $tier . ':' . CR; + + // Count raid bosses and add raid egg later if 2 or more bosses. + $bosscount = 0; + + // Get raid bosses for each raid level. + foreach($tier_pokemon as $raid_id_form) { + if(!isset($raid_id_form['id'])) continue; + $dex_id = $raid_id_form['id']; + $dex_form = 0; + if(isset($raid_id_form['temp_evolution_id'])) { + $dex_form = '-'.$raid_id_form['temp_evolution_id']; + }elseif(isset($raid_id_form['form'])) { + $dex_form = $raid_id_form['form']; + }else { + // If no form id is provided, let's check our db for normal form + $query_form_id = my_query('SELECT pokemon_form_id FROM pokemon WHERE pokedex_id = ? and pokemon_form_name = \'normal\' LIMIT 1', [$dex_id]); + if($query_form_id->rowCount() == 0) { + // If normal form doesn't exist in our db, use the smallest form id as a fallback + $query_form_id = my_query('SELECT min(pokemon_form_id) as pokemon_form_id FROM pokemon WHERE pokedex_id = ? LIMIT 1', [$dex_id]); + } + $result = $query_form_id->fetch(); + $dex_form = $result['pokemon_form_id']; } - // Clear old raid bosses. - if(strpos($arg, 'save#') === 0) { - debug_log('Disabling old raid bosses for levels: '. $clear); - disable_raid_level($clear); + $pokemon_arg = $dex_id . $dex_form; + + // Make sure we received a valid dex id. + if(!is_numeric($dex_id) || $dex_id == 0) { + info_log('Failed to get a valid pokemon dex id: '. $dex_id .' Continuing with next raid boss...'); + continue; } - // Init empty keys array. - $keys = []; - - // Raid tier array - debug_log('Processing the following raid levels:'); - debug_log($get_levels); - - // Process raid tier(s) - debug_log('Processing received ccev pogoinfo raid bosses for each raid level'); - foreach($data as $tier => $tier_pokemon) { - // Process raid level? - if(!in_array($tier,$get_levels)) { - continue; - } - // Raid level and message. - $msg .= '' . getTranslation('pokedex_raid_level') . SP . $tier . ':' . CR; - - // Count raid bosses and add raid egg later if 2 or more bosses. - $bosscount = 0; - - // Get raid bosses for each raid level. - foreach($tier_pokemon as $raid_id_form) { - $dex_id = $raid_id_form['id']; - $dex_form = 0; - if(isset($raid_id_form['temp_evolution_id'])) { - $dex_form = '-'.$raid_id_form['temp_evolution_id']; - }elseif(isset($raid_id_form['form'])) { - $dex_form = $raid_id_form['form']; - }else { - // If no form id is provided, let's check our db for normal form - $query_form_id = my_query("SELECT pokemon_form_id FROM pokemon WHERE pokedex_id='".$dex_id."' and pokemon_form_name='normal' LIMIT 1"); - if($query_form_id->rowCount() == 0) { - // If normal form doesn't exist in our db, use the smallest form id as a fallback - $query_form_id = my_query("SELECT min(pokemon_form_id) as pokemon_form_id FROM pokemon WHERE pokedex_id='".$dex_id."' LIMIT 1"); - } - $result = $query_form_id->fetch(); - $dex_form = $result['pokemon_form_id']; - } - - $pokemon_arg = $dex_id . $dex_form; - - // Get ID and form name used internally. - $local_pokemon = get_local_pokemon_name($dex_id, $dex_form); - debug_log('Got this pokemon dex id: ' . $dex_id); - debug_log('Got this pokemon dex form: ' . $dex_form); - debug_log('Got this local pokemon name and form: ' . $local_pokemon); - - // Make sure we received a valid dex id. - if(!is_numeric($dex_id) || $dex_id == 0) { - info_log('Failed to get a valid pokemon dex id: '. $dex_id .' Continuing with next raid boss...'); - continue; - } - - // Build new arg. - // Exclude 1 pokemon - if($poke1 == '0') { - $new_arg = $prefix . $pokemon_arg . ',0,0'; - - // Exclude 2 pokemon - } else if ($poke1 != '0' && $poke2 == '0') { - $new_arg = $prefix . $poke1 . ',' . $pokemon_arg . ',0'; - - // Exclude 3 pokemon - } else if ($poke1 != '0' && $poke2 != '0' && $poke3 == '0') { - $new_arg = $prefix . $poke1 . ',' . $poke2 . ',' . $pokemon_arg; - } - - // Exclude pokemon? - if($pokemon_arg == $poke1 || $pokemon_arg == $poke2 || $pokemon_arg == $poke3) { - // Add pokemon to exclude message. - $ex_msg .= $local_pokemon . SP . '(#' . $dex_id . ')' . CR; - - } else { - // Add pokemon to message. - $msg .= $local_pokemon . SP . '(#' . $dex_id . ')' . CR; - - // Counter. - $bosscount = $bosscount + 1; - - // Save to database? - if(strpos($arg, 'save#') === 0) { - // Update raid level of pokemon - my_query( - " - INSERT INTO raid_bosses (pokedex_id, pokemon_form_id, raid_level) - VALUES ('{$dex_id}', '{$dex_form}', '{$tier}') - " - ); - continue; - } - - // Are 3 raid bosses already selected? - if($poke1 == '0' || $poke2 == '0' || $poke3 == '0') { - // Add raid level to pokemon name - if($id == RAID_LEVEL_ALL) { - // Add key to exclude pokemon from import. - $keys[] = array( - 'text' => '[' . ($tier) . ']' . SP . $local_pokemon, - 'callback_data' => $id . ':pogoinfo:' . $new_arg - ); - } else { - // Add key to exclude pokemon from import. - $keys[] = array( - 'text' => $local_pokemon, - 'callback_data' => $id . ':pogoinfo:' . $new_arg - ); - } - } - } - } - - $msg .= CR; + // Save to database? + if($action == 's') { + // Update raid level of pokemon + my_query(' + INSERT INTO raid_bosses (pokedex_id, pokemon_form_id, raid_level) + VALUES (?, ?, ?) + ', [$dex_id, $dex_form, $tier] + ); } - // Get the inline key array. - $keys = inline_key_array($keys, 2); - - // Saved raid bosses? - if(strpos($arg, 'save#') === 0) { - // Get all pokemon with raid levels from database. - $rs = my_query( - " - SELECT raid_bosses.pokedex_id, raid_bosses.pokemon_form_id, raid_bosses.raid_level - FROM raid_bosses - LEFT JOIN pokemon - ON raid_bosses.pokedex_id = pokemon.pokedex_id - AND raid_bosses.pokemon_form_id = pokemon.pokemon_form_id - WHERE raid_bosses.raid_level IN ({$clear}) - AND raid_bosses.scheduled = 0 - ORDER BY raid_bosses.raid_level, raid_bosses.pokedex_id, pokemon.pokemon_form_name != 'normal', pokemon.pokemon_form_name, raid_bosses.pokemon_form_id - " - ); - - // Init empty keys array. - $keys = []; - - // Init message and previous. - $msg = 'Pogoinfo' . SP . '—' . SP . getTranslation('import_done') . '' . CR; - $previous = ''; - - // Build the message - while ($pokemon = $rs->fetch()) { - // Set current level - $current = $pokemon['raid_level']; - - // Add header for each raid level - if($previous != $current) { - $msg .= CR . '' . getTranslation($pokemon['raid_level'] . 'stars') . ':' . CR ; - } - - // Add pokemon with id and name. - $dex_id = $pokemon['pokedex_id']; - $pokemon_form_id = $pokemon['pokemon_form_id']; - $poke_name = get_local_pokemon_name($dex_id, $pokemon_form_id); - $msg .= $poke_name . ' (#' . $dex_id . ')' . CR; - - // Add button to edit pokemon. - if($id == RAID_LEVEL_ALL) { - $keys[] = array( - 'text' => '[' . $pokemon['raid_level'] . ']' . SP . $poke_name, - 'callback_data' => $dex_id . "-" . $pokemon_form_id . ':pokedex_edit_pokemon:0' - ); - } else { - $keys[] = array( - 'text' => $poke_name, - 'callback_data' => $dex_id . "-" . $pokemon_form_id . ':pokedex_edit_pokemon:0' - ); - } - - // Prepare next run. - $previous = $current; - } - - // Inline key array. - $keys = inline_key_array($keys, 2); - - // Navigation keys. - $nav_keys = []; - - // Abort button. - $nav_keys[] = array( - 'text' => getTranslation('done'), - 'callback_data' => '0:exit:1' - ); - - // Keys. - $nav_keys = inline_key_array($nav_keys, 1); - $keys = array_merge($keys, $nav_keys); - - // User is still on the import. + // Get ID and form name used internally. + $local_pokemon = get_local_pokemon_name($dex_id, $dex_form); + debug_log('Got this pokemon dex id: ' . $dex_id); + debug_log('Got this pokemon dex form: ' . $dex_form); + debug_log('Got this local pokemon name and form: ' . $local_pokemon); + + // Exclude pokemon? + if(in_array($pokemon_arg, $exclusions)) { + // Add pokemon to exclude message. + $ex_msg .= $local_pokemon . SP . '(#' . $dex_id . ')' . CR; + } else { - $msg .= '' . getTranslation('excluded_raid_bosses') . '' . CR; - $msg .= (empty($ex_msg) ? (getTranslation('none') . CR) : $ex_msg) . CR; - - // Import or select more pokemon to exclude? - if($poke1 == '0' || $poke2 == '0' || $poke3 == '0') { - $msg .= '' . getTranslation('exclude_raid_boss_or_import') . ':'; - } else { - $msg .= '' . getTranslation('import_raid_bosses') . ''; - } - - // Navigation keys. - $nav_keys = []; - - // Back button. - $nav_keys[] = array( - 'text' => getTranslation('back'), - 'callback_data' => '0:pogoinfo:0' - ); - - // Save button. - $nav_keys[] = array( - 'text' => EMOJI_DISK, - 'callback_data' => $id . ':pogoinfo:save#' . $poke1 . ',' . $poke2 . ',' . $poke3 - ); - - // Reset button. - if($poke1 != 0) { - $nav_keys[] = array( - 'text' => getTranslation('reset'), - 'callback_data' => $id . ':pogoinfo:ex#0,0,0' - ); - } - - // Abort button. - $nav_keys[] = array( - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ); - - // Get the inline key array and merge keys. - $nav_keys = inline_key_array($nav_keys, 2); - $keys = array_merge($keys, $nav_keys); + // Add pokemon to message. + $msg .= $local_pokemon . SP . '(#' . $dex_id . ')' . CR; + + // Counter. + $bosscount = $bosscount + 1; + + // Are 3 raid bosses already selected? + if(count($exclusions) == 3) continue; + + $keyText = $local_pokemon; + if($id == 'all') { + $keyText = '[' . ($tier) . ']' . SP . $local_pokemon; + } + $e = $exclusions; + $e[] = $pokemon_arg; + $keyAction = ($action == 's') ? + ['pokedex_edit_pokemon', 'p' => $dex_id . "-" . $dex_form] : + ['pogoinfo', 'rl' => $id, 'e' => implode('#', $e)]; + // Add key + $keys[] = button($keyText, $keyAction); } + } + $msg .= CR; +} + +// Get the inline key array. +$keys = inline_key_array($keys, 2); + +// Saved raid bosses? +if($action == 's') { + $msg .= '' . getTranslation('import_done') . '' . CR; + $msg .= CR . '' . getTranslation('pokedex_edit_pokemon') . ''; + + // Abort button. + $nav_keys[][] = button(getTranslation('done'), ['exit', 'd' => 1]); + +// User is still on the import. +} else { + $msg .= '' . getTranslation('excluded_raid_bosses') . '' . CR; + $msg .= (empty($ex_msg) ? (getTranslation('none') . CR) : $ex_msg) . CR; + + // Import or select more pokemon to exclude? + if(!isset($exclusions[2])) { + $msg .= '' . getTranslation('exclude_raid_boss_or_import') . ':'; + } else { + $msg .= '' . getTranslation('import_raid_bosses') . ''; + } + + // Navigation keys. + $nav_keys = []; + + // Back button. + $nav_keys[] = button(getTranslation('back'), 'pogoinfo'); + + // Save button. + $nav_keys[] = button(EMOJI_DISK, ['pogoinfo', 'rl' => $id, 'a' => 's', 'e' => implode('#', $exclusions)]); + + // Reset button. + if(isset($exclusions[0])) { + $nav_keys[] = button(getTranslation('reset'), ['pogoinfo', 'rl' => $id]); + } + + // Abort button. + $nav_keys[] = button(getTranslation('abort'), 'exit'); + + // Get the inline key array and merge keys. + $nav_keys = inline_key_array($nav_keys, 2); } +$keys = array_merge($keys, $nav_keys); // Callback message string. $callback_response = 'OK'; @@ -354,6 +238,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/pokebattler.php b/mods/pokebattler.php index 72620f77..b1b1b14f 100644 --- a/mods/pokebattler.php +++ b/mods/pokebattler.php @@ -1,382 +1,254 @@ accessCheck('pokedex'); include(LOGIC_PATH . '/resolve_boss_name_to_ids.php'); -// Levels available for import at PokeBattler -$levels = array('6', '5', '3', '1'); - // Get raid levels -$id = $data['id']; - -// Exclude pokemon -$arg = $data['arg']; +$id = $data['rl'] ?? 0; +$action = $data['a'] ?? ''; // Raid level selection if($id == 0) { - // Set message. - $msg = '' . getTranslation('import') . SP . '(Pokebattler)' . '' . CR . CR; - $msg .= '' . getTranslation('select_raid_level') . ':'; - - // Init empty keys array. - $keys = []; - - // All raid level keys. - $keys[][] = array( - 'text' => getTranslation('pokedex_all_raid_level'), - 'callback_data' => RAID_LEVEL_ALL . ':pokebattler:ex#0,0,0' - ); - - // Add key for each raid level - foreach($levels as $l) { - $keys[][] = array( - 'text' => getTranslation($l . 'stars'), - 'callback_data' => $l . ':pokebattler:ex#0,0,0' - ); - } - $keys[][] = array( - 'text' => getTranslation('1stars') . ' & ' . getTranslation('3stars'), - 'callback_data' => '3,1:pokebattler:ex#0,0,0' - ); - - // Add back and abort buttons - $keys[] = [ - [ - 'text' => getTranslation('back'), - 'callback_data' => '0:pokedex_import:0' - ], - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ]; - -} else if($id > 0) { - // Set message and init message to exclude raid bosses. - $msg = '' . getTranslation('import') . SP . '(Pokebattler)' . '' . CR . CR; - $ex_msg = ''; - - // Get pokebattler data. - debug_log('Getting raid bosses from pokebattler.com now...'); - $link = 'https://fight.pokebattler.com/raids'; - $pb_data = curl_get_contents($link); - $pb_data = json_decode($pb_data,true); - - // All raid levels? - if($id == RAID_LEVEL_ALL) { - $get_levels = $levels; - $clear = "'6','5','3','1'"; - } else { - $get_levels = explode(",", $id); - $clear = $id; - } + // Set message. + $msg = '' . getTranslation('import') . SP . '(Pokebattler)' . '' . CR . CR; + $msg .= '' . getTranslation('select_raid_level') . ':'; + + // Init empty keys array. + $keys = []; + + // All raid level keys. + $keys[][] = button(getTranslation('pokedex_all_raid_level'), ['pokebattler', 'rl' => implode(",", $pokebattler_levels)]); + + // Add key for each raid level + foreach($pokebattler_levels as $l) { + $keys[][] = button(getTranslation($l . 'stars'), ['pokebattler', 'rl' => $l]); + } + $keys[][] = button(getTranslation('1stars') . ' & ' . getTranslation('3stars'), ['pokebattler', 'rl' => '1,3']); + + // Add back and abort buttons + $keys[] = [ + button(getTranslation('back'), 'pokedex_import'), + button(getTranslation('abort'), 'exit') + ]; + // Callback message string. + $callback_response = 'OK'; + + // Telegram JSON array. + $tg_json = array(); + + // Answer callback. + $tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_response, true); + + // Edit message. + $tg_json[] = edit_message($update, $msg, $keys, false, true); + + // Telegram multicurl request. + curl_json_multi_request($tg_json); + exit; +} +// Set message and init message to exclude raid bosses. +$msg = '' . getTranslation('import') . SP . '(Pokebattler)' . '' . CR . CR; +$ex_msg = ''; + +// Get pokebattler data. +debug_log('Getting raid bosses from pokebattler.com now...'); +$link = 'https://fight.pokebattler.com/raids'; +$pb_data = curl_get_contents($link); +$pb_data = json_decode($pb_data,true); + +$get_levels = explode(',', $id); +$clear = $id; + +// New request +$exclusions = isset($data['e']) ? explode('#', $data['e']) : []; +if(!empty($exclusions[0])) { + debug_log('Excluded raid bosses: ' . implode(', ', $exclusions)); +} - // Prefix for exclusion. - $prefix = 'ex#'; - - // New request - if($arg == 'ex#0,0,0') { - $poke1 = 0; - $poke2 = 0; - $poke3 = 0; - - // Get raid bosses to exclude. - } else if(strpos($arg, 'ex#') === 0 || strpos($arg, 'save#') === 0) { - $poke_ids = explode('#', $arg); - $poke_ids = explode(',', $poke_ids[1]); - $poke1 = $poke_ids[0]; - $poke2 = $poke_ids[1]; - $poke3 = $poke_ids[2]; - debug_log('Excluded raid boss #1: ' . $poke1); - debug_log('Excluded raid boss #2: ' . $poke2); - debug_log('Excluded raid boss #3: ' . $poke3); - } +// Clear old raid bosses. +if($action == 's') { + require_once(LOGIC_PATH . '/disable_raid_level.php'); + debug_log('Disabling old raid bosses for levels: '. $clear); + disable_raid_level($clear); +} - // Clear old raid bosses. - if(strpos($arg, 'save#') === 0) { - debug_log('Disabling old raid bosses for levels: '. $clear); - disable_raid_level($clear); - } +// Init empty keys array. +$keys = []; - // Init empty keys array. - $keys = []; - - // Raid tier array - debug_log('Processing the following raid levels:'); - $raidlevels = array(); - foreach($get_levels as $level) { - if($level == 6) { - $level = 'MEGA'; // PB uses RAID_LEVEL_MEGA instead of RAID_LEVEL_6 - } - $raidlevels[] = 'RAID_LEVEL_' . $level; - } - debug_log($raidlevels); - $levels_processed = []; - $bosses = []; - // Process breaking news section - $now = new DateTime('now', new DateTimeZone($config->TIMEZONE)); - $ph = new dateTimeZone('America/Phoenix'); - foreach($pb_data['breakingNews'] as $news) { - if($news['type'] == 'RAID_TYPE_RAID') { - $rl = str_replace('RAID_LEVEL_','', $news['tier']); - if($rl == "MEGA") $raid_level_id = 6; else $raid_level_id = $rl; - $starttime = new DateTime("@".substr($news['startDate'],0,10)); - $endtime = new DateTime("@".substr($news['endDate'],0,10)); - $starttime->setTimezone($ph); - $endtime->setTimezone($ph); - - if(in_array($news['tier'], $raidlevels) && $starttime->getTimestamp() < $now->getTimestamp() && $endtime->getTimestamp() > $now->getTimestamp()) { - $levels_processed[$raid_level_id] = $news['tier']; - $dex_id_form = resolve_boss_name_to_ids($news['pokemon']); - $bosses[$raid_level_id][] = ['id' => $dex_id_form, 'shiny' => $news['shiny']]; - } - } +// Raid tier array +debug_log('Processing the following raid levels:'); +$raidlevels = array(); +foreach($get_levels as $level) { + $raidlevels[] = 'RAID_LEVEL_' . $pokebattler_level_map[$level]; +} +debug_log($raidlevels); +$levels_processed = $bosses = []; +// Process breaking news section +$now = new DateTime('now', new DateTimeZone($config->TIMEZONE)); +$ph = new dateTimeZone('America/Los_Angeles'); +foreach($pb_data['breakingNews'] as $news) { + if($news['type'] != 'RAID_TYPE_RAID') continue; + + $rl = str_replace('RAID_LEVEL_','', $news['tier']); + $raid_level_id = array_search($rl, $pokebattler_level_map); + $starttime = new DateTime("@".substr($news['startDate'],0,10)); + $endtime = new DateTime("@".substr($news['endDate'],0,10)); + $starttime->setTimezone($ph); + $endtime->setTimezone($ph); + + $tierHolder = $news['tier']; + [$dex_id, $form_id] = resolve_boss_name_to_ids($news['pokemon']); + if(in_array($dex_id, PRIMAL_MONS) && $news['tier'] == 'RAID_LEVEL_MEGA_5') { + $raid_level_id = 10; + $tierHolder = 'RAID_LEVEL_PRIMAL'; + }elseif(in_array($form_id, [-1,-2,-3]) && $news['tier'] == 'RAID_LEVEL_MEGA_5') { + $raid_level_id = 16; + $tierHolder = 'RAID_LEVEL_SUPER_MEGA_4'; + } + if(in_array($tierHolder, $raidlevels) && $starttime->getTimestamp() < $now->getTimestamp() && $endtime->getTimestamp() > $now->getTimestamp()) { + $levels_processed[$raid_level_id] = $news['tier']; + $bosses[$raid_level_id][] = ['dex_id' => $dex_id, 'form_id' => $form_id, 'shiny' => $news['shiny']]; + } +} +// Process raid tier(s) +debug_log('Processing received pokebattler raid bosses for each raid level'); +foreach($pb_data['tiers'] as $tier) { + $rl = str_replace('RAID_LEVEL_','', $tier['tier']); + $raid_level_id = array_search($rl, $pokebattler_level_map); + // Skip this raid level if the boss data was already collected from breaking news or raid level doesn't interest us + if(!in_array($tier['tier'], $raidlevels) or isset($levels_processed[$raid_level_id])) { + continue; + } + // Get raid bosses for each raid level. + foreach($tier['raids'] as $raid) { + // Raid level + if (!array_key_exists('id', $raid) or $raid['id'] == 0) { + debug_log('Skipping raid boss ' . $raid['pokemon'] . ' since it has no id, it\'s likely in the future!'); + continue; } + [$dex_id, $form_id] = resolve_boss_name_to_ids($raid['pokemon']); + $bosses[$raid_level_id][] = ['dex_id' => $dex_id, 'form_id' => $form_id, 'shiny' => $raid['shiny']]; + } +} - // Process raid tier(s) - debug_log('Processing received pokebattler raid bosses for each raid level'); - foreach($pb_data['tiers'] as $tier) { - $rl = str_replace('RAID_LEVEL_','', $tier['tier']); - if($rl == "MEGA") $raid_level_id = 6; else $raid_level_id = $rl; - // Skip this raid level if the boss data was already collected from breaking news or raid level doesn't interest us - if(!in_array($tier['tier'], $raidlevels) or isset($levels_processed[$raid_level_id])) { - continue; - } - // Get raid bosses for each raid level. - foreach($tier['raids'] as $raid) { - // Raid level - if ($raid['id'] == 0) { - debug_log("Skipping raid boss {$raid['pokemon']} since it has no id, it's likely in the future!"); - continue; - } - $dex_id_form = resolve_boss_name_to_ids($raid['pokemon']); - $bosses[$raid_level_id][] = ['id' => $dex_id_form, 'shiny' => $raid['shiny']]; - } +foreach($get_levels as $raid_level_id) { + if(!isset($bosses[$raid_level_id])) continue; + if($raid_level_id > 5) $raid_level_text = getTranslation($raid_level_id . 'stars_short'); else $raid_level_text = $raid_level_id; + $msg .= '' . getTranslation('pokedex_raid_level') . SP . $raid_level_text . ':' . CR; + foreach($bosses[$raid_level_id] as $dex_id_form) { + $dex_id = $dex_id_form['dex_id']; + $dex_form = $dex_id_form['form_id']; + $pokemon_arg = $dex_id . (($dex_form != 'normal') ? ('-' . $dex_form) : '-0'); + $local_pokemon = get_local_pokemon_name($dex_id, $dex_form); + debug_log('Got this pokemon dex id: ' . $dex_id); + debug_log('Got this pokemon dex form: ' . $dex_form); + debug_log('Got this local pokemon name and form: ' . $local_pokemon); + + // Make sure we received a valid dex id. + if(!is_numeric($dex_id) || $dex_id == 0) { + info_log('Failed to get a valid pokemon dex id: '. $dex_id .' Continuing with next raid boss...'); + continue; } - $count = count($get_levels)-1; - for($i=$count;$i>=0;$i--) { - $raid_level_id = $get_levels[$i]; - if($raid_level_id == 6) $rl = "MEGA"; else $rl = $raid_level_id; - $msg .= '' . getTranslation('pokedex_raid_level') . SP . $rl . ':' . CR; - foreach($bosses[$raid_level_id] as $dex_id_form) { - $dex_id = explode('-', $dex_id_form['id'], 2)[0]; - $dex_form = explode('-', $dex_id_form['id'], 2)[1]; - $pokemon_arg = $dex_id . (($dex_form != 'normal') ? ('-' . $dex_form) : '-0'); - $local_pokemon = get_local_pokemon_name($dex_id, $dex_form); - debug_log('Got this pokemon dex id: ' . $dex_id); - debug_log('Got this pokemon dex form: ' . $dex_form); - debug_log('Got this local pokemon name and form: ' . $local_pokemon); - - // Make sure we received a valid dex id. - if(!is_numeric($dex_id) || $dex_id == 0) { - info_log('Failed to get a valid pokemon dex id: '. $dex_id .' Continuing with next raid boss...'); - continue; - } - - // Build new arg. - // Exclude 1 pokemon - if($poke1 == '0') { - $new_arg = $prefix . $pokemon_arg . ',0,0'; - - // Exclude 2 pokemon - } else if ($poke1 != '0' && $poke2 == '0') { - $new_arg = $prefix . $poke1 . ',' . $pokemon_arg . ',0'; - - // Exclude 3 pokemon - } else if ($poke1 != '0' && $poke2 != '0' && $poke3 == '0') { - $new_arg = $prefix . $poke1 . ',' . $poke2 . ',' . $pokemon_arg; - } - - // Exclude pokemon? - if($pokemon_arg == $poke1 || $pokemon_arg == $poke2 || $pokemon_arg == $poke3) { - // Add pokemon to exclude message. - $ex_msg .= $local_pokemon . SP . '(#' . $dex_id . ')' . CR; - - } else { - // Add pokemon to message. - $msg .= $local_pokemon . SP . '(#' . $dex_id . ')' . CR; - - // Shiny? - $shiny = 0; - if($dex_id_form['shiny'] == 'true') { - $shiny = 1; - } - - // Save to database? - if(strpos($arg, 'save#') === 0) { - // Update raid level of pokemon - my_query( - " - UPDATE pokemon - SET shiny = {$shiny} - WHERE pokedex_id = {$dex_id} - AND pokemon_form_id = '{$dex_form}' - " - ); - my_query( - " - INSERT INTO raid_bosses (pokedex_id, pokemon_form_id, raid_level) - VALUES ('{$dex_id}', '{$dex_form}', '{$raid_level_id}') - " - ); - continue; - } - - // Are 3 raid bosses already selected? - if($poke1 == '0' || $poke2 == '0' || $poke3 == '0') { - // Add raid level to pokemon name - if($id == RAID_LEVEL_ALL) { - // Add key to exclude pokemon from import. - $keys[] = array( - 'text' => '[' . ($rl) . ']' . SP . $local_pokemon, - 'callback_data' => $id . ':pokebattler:' . $new_arg - ); - } else { - // Add key to exclude pokemon from import. - $keys[] = array( - 'text' => $local_pokemon, - 'callback_data' => $id . ':pokebattler:' . $new_arg - ); - } - } - } - } - $msg .= CR; + // Save to database? + if($action == 's') { + // Shiny? + $shiny = ($dex_id_form['shiny'] == 'true') ? 1 : 0; + // Update raid level of pokemon + my_query(' + UPDATE pokemon + SET shiny = ? + WHERE pokedex_id = ? + AND pokemon_form_id = ? + ', [$shiny, $dex_id, $dex_form] + ); + my_query(' + INSERT INTO raid_bosses (pokedex_id, pokemon_form_id, raid_level) + VALUES (?, ?, ?) + ', [$dex_id, $dex_form, $raid_level_id] + ); } - // Get the inline key array. - $keys = inline_key_array($keys, 2); - - // Saved raid bosses? - if(strpos($arg, 'save#') === 0) { - // Get all pokemon with raid levels from database. - $rs = my_query( - " - SELECT raid_bosses.pokedex_id, raid_bosses.pokemon_form_id, raid_bosses.raid_level - FROM raid_bosses - LEFT JOIN pokemon - ON raid_bosses.pokedex_id = pokemon.pokedex_id - AND raid_bosses.pokemon_form_id = pokemon.pokemon_form_id - WHERE raid_bosses.raid_level IN ({$clear}) - AND raid_bosses.scheduled = 0 - ORDER BY raid_bosses.raid_level, raid_bosses.pokedex_id, pokemon.pokemon_form_name != 'normal', pokemon.pokemon_form_name, raid_bosses.pokemon_form_id - " - ); - - // Init empty keys array. - $keys = []; - - // Init message and previous. - $msg = 'Pokebattler' . SP . '—' . SP . getTranslation('import_done') . '' . CR; - $previous = ''; - - // Build the message - while ($pokemon = $rs->fetch()) { - // Set current level - $current = $pokemon['raid_level']; - - // Add header for each raid level - if($previous != $current) { - $msg .= CR . '' . getTranslation($pokemon['raid_level'] . 'stars') . ':' . CR ; - } - - // Add pokemon with id and name. - $dex_id = $pokemon['pokedex_id']; - $pokemon_form_id = $pokemon['pokemon_form_id']; - $poke_name = get_local_pokemon_name($dex_id, $pokemon_form_id); - $msg .= $poke_name . ' (#' . $dex_id . ')' . CR; - - // Add button to edit pokemon. - if($id == RAID_LEVEL_ALL) { - $keys[] = array( - 'text' => '[' . $pokemon['raid_level'] . ']' . SP . $poke_name, - 'callback_data' => $dex_id . "-" . $pokemon_form_id . ':pokedex_edit_pokemon:0' - ); - } else { - $keys[] = array( - 'text' => $poke_name, - 'callback_data' => $dex_id . "-" . $pokemon_form_id . ':pokedex_edit_pokemon:0' - ); - } - - // Prepare next run. - $previous = $current; - } - - // Message. - $msg .= CR . '' . getTranslation('pokedex_edit_pokemon') . ''; - - // Inline key array. - $keys = inline_key_array($keys, 2); - - // Navigation keys. - $nav_keys = []; - - // Abort button. - $nav_keys[] = array( - 'text' => getTranslation('done'), - 'callback_data' => '0:exit:1' - ); - - // Keys. - $nav_keys = inline_key_array($nav_keys, 1); - $keys = array_merge($keys, $nav_keys); - - // User is still on the import. + // Exclude pokemon? + if(in_array($pokemon_arg, $exclusions)) { + // Add pokemon to exclude message. + $ex_msg .= $local_pokemon . SP . '(#' . $dex_id . ')' . CR; + } else { - $msg .= '' . getTranslation('excluded_raid_bosses') . '' . CR; - $msg .= (empty($ex_msg) ? (getTranslation('none') . CR) : $ex_msg) . CR; - - // Import or select more pokemon to exclude? - if($poke1 == '0' || $poke2 == '0' || $poke3 == '0') { - $msg .= '' . getTranslation('exclude_raid_boss_or_import') . ':'; - } else { - $msg .= '' . getTranslation('import_raid_bosses') . ''; - } - - // Navigation keys. - $nav_keys = []; - - // Back button. - $nav_keys[] = array( - 'text' => getTranslation('back'), - 'callback_data' => '0:pokebattler:0' - ); - - // Save button. - $nav_keys[] = array( - 'text' => EMOJI_DISK, - 'callback_data' => $id . ':pokebattler:save#' . $poke1 . ',' . $poke2 . ',' . $poke3 - ); - - // Reset button. - if($poke1 != 0) { - $nav_keys[] = array( - 'text' => getTranslation('reset'), - 'callback_data' => $id . ':pokebattler:ex#0,0,0' - ); - } - - // Abort button. - $nav_keys[] = array( - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ); - - // Get the inline key array and merge keys. - $nav_keys = inline_key_array($nav_keys, 2); - $keys = array_merge($keys, $nav_keys); + // Add pokemon to message. + $msg .= $local_pokemon . SP . '(#' . $dex_id . ')' . CR; + + // Add key to exclude pokemon from import. + $button_text_prefix = ''; + if($id == implode(',', $pokebattler_levels)) { + // Add raid level to pokemon name + $button_text_prefix = '[' . ($raid_level_text) . ']'; + } + $e = $exclusions; + $e[] = $pokemon_arg; + // Are 3 raid bosses already selected? + if(count($exclusions) == 3) continue; + $keyAction = ($action == 's') ? + ['pokedex_edit_pokemon', 'p' => $dex_id . "-" . $dex_form] : + ['pokebattler', 'rl' => $id, 'e' => implode('#', $e)]; + $keys[] = button($button_text_prefix . SP . $local_pokemon, $keyAction); } + } + $msg .= CR; } +// Get the inline key array. +$keys = inline_key_array($keys, 2); + +// Saved raid bosses? +if($action == 's') { + $msg .= '' . getTranslation('import_done') . '' . CR; + $msg .= CR . '' . getTranslation('pokedex_edit_pokemon') . ''; + + // Abort button. + $nav_keys[][] = button(getTranslation('done'), ['exit', 'd' => 1]); + +// User is still on the import. +} else { + $msg .= '' . getTranslation('excluded_raid_bosses') . '' . CR; + $msg .= (empty($ex_msg) ? (getTranslation('none') . CR) : $ex_msg) . CR; + + // Import or select more pokemon to exclude? + if(!isset($exclusions[2])) { + $msg .= '' . getTranslation('exclude_raid_boss_or_import') . ':'; + } else { + $msg .= '' . getTranslation('import_raid_bosses') . ''; + } + + // Navigation keys. + $nav_keys = []; + + // Back button. + $nav_keys[] = button(getTranslation('back'), 'pokebattler'); + + // Save button. + $nav_keys[] = button(EMOJI_DISK, ['pokebattler', 'rl' => $id, 'a' => 's', 'e' => implode('#', $exclusions)]); + + // Reset button. + if(isset($exclusions[0])) { + $nav_keys[] = button(getTranslation('reset'), ['pokebattler', 'rl' => $id]); + } + + // Abort button. + $nav_keys[] = button(getTranslation('abort'), 'exit'); + + // Get the inline key array and merge keys. + $nav_keys = inline_key_array($nav_keys, 2); +} +$keys = array_merge($keys, $nav_keys); // Callback message string. $callback_response = 'OK'; @@ -391,6 +263,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/pokedex.php b/mods/pokedex.php index ed05d4dd..06fef626 100644 --- a/mods/pokedex.php +++ b/mods/pokedex.php @@ -1,47 +1,37 @@ accessCheck('pokedex'); // Get the limit. -$limit = $data['id']; +$limit = $data['l'] ?? 0; -// Get the action. -$action = $data['arg']; +// Set message. +$msg = getTranslation('pokedex_list_of_all') . CR . CR . '' . getTranslation('pokedex_edit_pokemon') . ''; -if ($update['callback_query']['message']['chat']['type'] == 'private') { - // Set message. - $msg = getTranslation('pokedex_list_of_all') . CR . CR . '' . getTranslation('pokedex_edit_pokemon') . ''; +// Get pokemon. +$keys = edit_pokedex_keys($limit); - // Get pokemon. - $keys = edit_pokedex_keys($limit, $action); +// Empty keys? +if (!$keys) { + $msg = getTranslation('pokedex_not_found'); +} - // Empty keys? - if (!$keys) { - $msg = getTranslation('pokedex_not_found'); - } +// Telegram JSON array. +$tg_json = array(); - // Build callback message string. - $callback_response = 'OK'; +// Answer callback. +$tg_json[] = answerCallbackQuery($update['callback_query']['id'], 'OK', true); - // Telegram JSON array. - $tg_json = array(); +// Edit message. +$tg_json[] = edit_message($update, $msg, $keys, false, true); - // Answer callback. - $tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_response, true); - - // Edit message. - $tg_json[] = edit_message($update, $msg, $keys, false, true); - - // Telegram multicurl request. - curl_json_multi_request($tg_json); -} - -// Exit. -exit(); +// Telegram multicurl request. +curl_json_multi_request($tg_json); diff --git a/mods/pokedex_disable_raids.php b/mods/pokedex_disable_raids.php index 0e4db6a6..300324fb 100644 --- a/mods/pokedex_disable_raids.php +++ b/mods/pokedex_disable_raids.php @@ -7,135 +7,114 @@ //debug_log($data); // Check access. -bot_access_check($update, 'pokedex'); +$botUser->accessCheck('pokedex'); // Get raid levels. -$id = $data['id']; +$id = $data['rl'] ?? 0; // Get argument. -$arg = $data['arg']; +$arg = $data['a'] ?? 0; + +// Specify raid levels. +$levels = RAID_LEVEL_ALL; // All raid levels? -if($id == 'X' . RAID_LEVEL_ALL) { - // TODO(artanicus): get this from somewhere instead of hardcoded - $clear = "'X','6','5','3','1'"; +if($id == 'all') { + $clear = "'" . implode("','", $levels) . "'"; } else { - $clear = "'" . $id . "'"; + $clear = "'" . $id . "'"; } -// Specify raid levels. -// TODO(artanicus): get this from somewhere instead of hardcoded -$levels = array('X', '6', '5', '3', '1'); - // Raid level selection if($arg == 0) { - // Set message. - $msg = '' . getTranslation('disable_raid_level') . ':'; - - // Init empty keys array. - $keys = []; - - // All raid level keys. - $keys[] = array( - 'text' => getTranslation('pokedex_all_raid_level'), - 'callback_data' => 'X' . RAID_LEVEL_ALL . ':pokedex_disable_raids:1' - ); - - // Add key for each raid level - foreach($levels as $l) { - $keys[] = array( - 'text' => getTranslation($l . 'stars'), - 'callback_data' => $l . ':pokedex_disable_raids:1' - ); - } + // Set message. + $msg = '' . getTranslation('disable_raid_level') . ':'; + + // Init empty keys array. + $keys = []; - // Add abort button - $keys[] = array( - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ); + // All raid level keys. + $keys[][] = button(getTranslation('pokedex_all_raid_level'), ['pokedex_disable_raids', 'rl' => 'all', 'a' => 1]); - // Get the inline key array. - $keys = inline_key_array($keys, 1); + // Add key for each raid level + foreach($levels as $l) { + $keys[][] = button(getTranslation($l . 'stars'), ['pokedex_disable_raids', 'rl' => $l, 'a' => 1]); + } + + // Add abort button + $keys[][] = button(getTranslation('abort'), 'exit'); // Confirmation to disable raid level } else if($arg == 1) { - // Get all pokemon with raid levels from database. - $rs = my_query( - " - SELECT pokemon.pokedex_id, pokemon.pokemon_form_name, pokemon.pokemon_form_id, raid_bosses.raid_level - FROM raid_bosses - LEFT JOIN pokemon - ON pokemon.pokedex_id = raid_bosses.pokedex_id - AND pokemon.pokemon_form_id = raid_bosses.pokemon_form_id - WHERE raid_bosses.raid_level IN ({$clear}) - AND raid_bosses.scheduled = 0 - ORDER BY raid_level, pokedex_id, pokemon_form_name != 'normal', pokemon_form_name - " - ); - - // Init empty keys array. - $keys = []; - - // Init message and previous. - $msg = '' . getTranslation('disable_raid_level') . '?' . CR; - $previous = ''; - - // Build the message - while ($pokemon = $rs->fetch()) { - // Set current level - $current = $pokemon['raid_level']; - - // Add header for each raid level - if($previous != $current) { - $msg .= CR . '' . getTranslation($pokemon['raid_level'] . 'stars') . ':' . CR ; - } - - // Add pokemon with id and name. - $dex_id = $pokemon['pokedex_id']; - $poke_name = get_local_pokemon_name($dex_id, $pokemon['pokemon_form_id']); - $msg .= $poke_name . ' (#' . $dex_id . ')' . CR; - - // Prepare next run. - $previous = $current; + // Get all pokemon with raid levels from database. + $rs = my_query(' + SELECT pokemon.pokedex_id, pokemon.pokemon_form_name, pokemon.pokemon_form_id, raid_bosses.raid_level + FROM raid_bosses + LEFT JOIN pokemon + ON pokemon.pokedex_id = raid_bosses.pokedex_id + AND pokemon.pokemon_form_id = raid_bosses.pokemon_form_id + WHERE raid_bosses.raid_level IN (' . $clear . ') + AND raid_bosses.scheduled = 0 + ORDER BY raid_level, pokedex_id, pokemon_form_name != \'normal\', pokemon_form_name + '); + + // Init empty keys array. + $keys = []; + + // Init message and previous. + $msg = '' . getTranslation('disable_raid_level') . '?' . CR; + $previous = ''; + + // Build the message + while ($pokemon = $rs->fetch()) { + // Set current level + $current = $pokemon['raid_level']; + + // Add header for each raid level + if($previous != $current) { + $msg .= CR . '' . getTranslation($pokemon['raid_level'] . 'stars') . ':' . CR ; } - // Disable. - $keys[] = array( - 'text' => getTranslation('yes'), - 'callback_data' => $id . ':pokedex_disable_raids:2' - ); + // Add pokemon with id and name. + $dex_id = $pokemon['pokedex_id']; + $poke_name = get_local_pokemon_name($dex_id, $pokemon['pokemon_form_id']); + $msg .= $poke_name . ' (#' . $dex_id . ')' . CR; + + // Prepare next run. + $previous = $current; + } + + // Disable. + $keys[] = button(getTranslation('yes'), ['pokedex_disable_raids', 'rl' => $id, 'a' => 2]); - // Abort. - $keys[] = array( - 'text' => getTranslation('no'), - 'callback_data' => '0:exit:0' - ); + // Abort. + $keys[] = button(getTranslation('no'), 'exit'); - // Inline keys array. - $keys = inline_key_array($keys, 2); + // Inline keys array. + $keys = inline_key_array($keys, 2); // Disable raid level } else if($arg == 2) { - debug_log('Disabling old raid bosses for levels: '. $clear); - disable_raid_level($clear); + require_once(LOGIC_PATH . '/disable_raid_level.php'); + debug_log('Disabling old raid bosses for levels: '. $clear); + disable_raid_level($clear); - // Message. - $msg = '' . getTranslation('disabled_raid_level') . ':' . CR; + // Message. + $msg = '' . getTranslation('disabled_raid_level') . ':' . CR; - // All levels - if($id == 'X' . RAID_LEVEL_ALL) { - foreach($levels as $lv) { - $msg .= getTranslation($lv . 'stars') . CR; - } + // All levels + if($id == 'all') { + foreach($levels as $lv) { + $msg .= getTranslation($lv . 'stars') . CR; + } - // Specific level - } else { - $msg .= getTranslation($id . 'stars'); - } + // Specific level + } else { + $msg .= getTranslation($id . 'stars'); + } - // Empty keys. - $keys = []; + // Empty keys. + $keys = []; } // Build callback message string. @@ -152,6 +131,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/pokedex_edit_pokemon.php b/mods/pokedex_edit_pokemon.php index 59156f5f..aa3071af 100644 --- a/mods/pokedex_edit_pokemon.php +++ b/mods/pokedex_edit_pokemon.php @@ -1,119 +1,60 @@ accessCheck('pokedex'); // Set the id. -$poke_id_form = $data['id']; -$dex_id_form = explode('-',$data['id'],2); -$pokedex_id = $dex_id_form[0]; -$pokemon_form = $dex_id_form[1]; - -// Set the arg. -$arg = $data['arg']; - -// Init empty keys array. -$keys = []; +$poke_id_form = $data['p']; +[$pokedex_id, $pokemon_form_id] = explode('-',$poke_id_form,2); // Set the message. -$msg = get_pokemon_info($pokedex_id, $pokemon_form); +$pokemon = get_pokemon_info($pokedex_id, $pokemon_form_id); +$poke_cp = get_formatted_pokemon_cp($pokemon); +$msg = getTranslation('raid_boss') . ': ' . get_local_pokemon_name($pokedex_id, $pokemon_form_id) . ' (#' . $pokedex_id . ')' . CR . CR; +$msg .= getTranslation('pokedex_raid_level') . ': ' . getTranslation($pokemon['raid_level'] . 'stars') . CR; +$msg .= (empty($poke_cp)) ? (getTranslation('pokedex_cp') . CR) : $poke_cp . CR; +$msg .= getTranslation('pokedex_weather') . ': ' . get_weather_icons($pokemon['weather']) . CR; +$msg .= (($pokemon['shiny'] == 1) ? (EMOJI_SHINY . SP . getTranslation('shiny')) : (getTranslation('not_shiny'))) . CR . CR; $msg .= '' . getTranslation('pokedex_select_action') . ''; // Create keys array. -$keys = [ - [ - [ - 'text' => getTranslation('pokedex_raid_level'), - 'callback_data' => $poke_id_form . ':pokedex_set_raid_level:setlevel' - ] - ] -]; +$keys[][] = button(getTranslation('pokedex_raid_level'), ['pokedex_set_raid_level', 'p' => $poke_id_form]); // Raid-Egg? Hide specific options! -$eggs = $GLOBALS['eggs']; -if(!in_array($pokedex_id, $eggs)) { - $keys_cp_weather = [ - [ - [ - 'text' => getTranslation('pokedex_min_cp'), - 'callback_data' => $poke_id_form . ':pokedex_set_cp:min-20-add-0' - ] - ], - [ - [ - 'text' => getTranslation('pokedex_max_cp'), - 'callback_data' => $poke_id_form . ':pokedex_set_cp:max-20-add-0' - ] - ], - [ - [ - 'text' => getTranslation('pokedex_min_weather_cp'), - 'callback_data' => $poke_id_form . ':pokedex_set_cp:min-25-add-0' - ] - ], - [ - [ - 'text' => getTranslation('pokedex_max_weather_cp'), - 'callback_data' => $poke_id_form . ':pokedex_set_cp:max-25-add-0' - ] - ], - [ - [ - 'text' => getTranslation('pokedex_weather'), - 'callback_data' => $poke_id_form . ':pokedex_set_weather:add-0' - ] - ], - [ - [ - 'text' => getTranslation('shiny'), - 'callback_data' => $poke_id_form . ':pokedex_set_shiny:setshiny' - ] - ] - ]; - - $keys = array_merge($keys, $keys_cp_weather); +if(!in_array($pokedex_id, EGGS)) { + $keys[][] = button(getTranslation('pokedex_min_cp'), ['pokedex_set_cp', 'p' => $poke_id_form, 'a' => 'add', 'l' => 20, 't' => 'min']); + $keys[][] = button(getTranslation('pokedex_max_cp'), ['pokedex_set_cp', 'p' => $poke_id_form, 'a' => 'add', 'l' => 20, 't' => 'max']); + $keys[][] = button(getTranslation('pokedex_min_weather_cp'), ['pokedex_set_cp', 'p' => $poke_id_form, 'a' => 'add', 'l' => 25, 't' => 'min']); + $keys[][] = button(getTranslation('pokedex_max_weather_cp'), ['pokedex_set_cp', 'p' => $poke_id_form, 'a' => 'add', 'l' => 25, 't' => 'max']); + $keys[][] = button(getTranslation('pokedex_weather'), ['pokedex_set_weather', 'p' => $poke_id_form]); + $keys[][] = button(getTranslation('shiny'), ['pokedex_set_shiny', 'p' => $poke_id_form]); } // Back and abort. $keys[] = [ - [ - 'text' => getTranslation('back'), - 'callback_data' => '0:pokedex:0' - ], - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] + button(getTranslation('back'), 'pokedex'), + button(getTranslation('abort'), 'exit') ]; -// Send message. -if($arg == 'id-or-name') { - // Send message. - send_message($update['message']['chat']['id'], $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); +// Build callback message string. +$callback_response = 'OK'; -// Edit message. -} else { - // Build callback message string. - $callback_response = 'OK'; - - // Telegram JSON array. - $tg_json = array(); - - // Answer callback. - $tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_response, true); +// Telegram JSON array. +$tg_json = array(); - // Edit message. - $tg_json[] = edit_message($update, $msg, $keys, false, true); +// Answer callback. +$tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_response, true); - // Telegram multicurl request. - curl_json_multi_request($tg_json); -} +// Edit message. +$tg_json[] = edit_message($update, $msg, $keys, false, true); -// Exit. -exit(); +// Telegram multicurl request. +curl_json_multi_request($tg_json); diff --git a/mods/pokedex_import.php b/mods/pokedex_import.php index a500a65f..f775e023 100644 --- a/mods/pokedex_import.php +++ b/mods/pokedex_import.php @@ -7,47 +7,17 @@ //debug_log($data); // Check access. -bot_access_check($update, 'pokedex'); +$botUser->accessCheck('pokedex'); -$id = $data['id']; -$arg = $data['arg']; +$msg = 'Import data from community maintained sources:'.CR; +$msg.= 'ccev\'s github repository'.CR; +$msg.= 'Pokebattler'; -$msg = "Import data from community maintained sources:".CR; -$msg.= "ccev's github repository".CR; -$msg.= "Pokebattler"; - -$keys = [ - [ - [ - 'text' => getTranslation('import') . SP . '(Pokebattler)', - 'callback_data' => '0:pokebattler:0' - ] - ], - [ - [ - 'text' => getTranslation('import') . SP . getTranslation('upcoming') . SP . '(Pokebattler)', - 'callback_data' => '0:import_future_bosses:0' - ] - ], - [ - [ - 'text' => getTranslation('import') . SP . getTranslation('shiny') . SP . '(Pokebattler)', - 'callback_data' => '0:import_shinyinfo:0' - ] - ], - [ - [ - 'text' => getTranslation('import') . SP . '(ccev pogoinfo)', - 'callback_data' => '0:pogoinfo:0' - ] - ], - [ - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ] - ]; +$keys[][] = button(getTranslation('import') . SP . '(Pokebattler)', 'pokebattler'); +$keys[][] = button(getTranslation('import') . SP . getTranslation('upcoming') . SP . '(Pokebattler)', 'import_future_bosses'); +$keys[][] = button(getTranslation('import') . SP . getTranslation('shiny') . SP . '(Pokebattler)', 'import_shinyinfo'); +$keys[][] = button(getTranslation('import') . SP . '(ccev pogoinfo)', 'pogoinfo'); +$keys[][] = button(getTranslation('abort'), 'exit'); // Callback message string. $callback_response = 'OK'; @@ -64,7 +34,4 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); -$dbh = null; exit(); - -?> \ No newline at end of file diff --git a/mods/pokedex_list_raids.php b/mods/pokedex_list_raids.php index cad3b361..e6dac581 100644 --- a/mods/pokedex_list_raids.php +++ b/mods/pokedex_list_raids.php @@ -7,19 +7,24 @@ //debug_log($data); // Check access. -bot_access_check($update, 'pokedex'); +$botUser->accessCheck('pokedex'); // Get all pokemon with raid levels from database. -$rs = my_query( - " - SELECT raid_bosses.id, raid_bosses.pokedex_id, raid_bosses.pokemon_form_id, raid_bosses.raid_level, raid_bosses.date_start, raid_bosses.date_end, raid_bosses.scheduled - FROM raid_bosses - LEFT JOIN pokemon - ON raid_bosses.pokedex_id = pokemon.pokedex_id - AND raid_bosses.pokemon_form_id = pokemon.pokemon_form_id - ORDER BY raid_bosses.date_start, raid_bosses.date_end, raid_bosses.raid_level, raid_bosses.pokedex_id, pokemon.pokemon_form_name != 'normal', pokemon.pokemon_form_name, raid_bosses.pokemon_form_id - " - ); +$rs = my_query(' + SELECT raid_bosses.id, + raid_bosses.pokedex_id, + raid_bosses.pokemon_form_id, + raid_bosses.raid_level, + DATE_FORMAT(date_start, \'%e.%c. ' . getTranslation('raid_egg_opens_at') . ' %H:%i\') as date_start, + DATE_FORMAT(date_end, \'%e.%c. ' . getTranslation('raid_egg_opens_at') . ' %H:%i\') as date_end, + raid_bosses.scheduled, + raid_bosses.disabled + FROM raid_bosses + LEFT JOIN pokemon + ON raid_bosses.pokedex_id = pokemon.pokedex_id + AND raid_bosses.pokemon_form_id = pokemon.pokemon_form_id + ORDER BY raid_bosses.date_start, raid_bosses.date_end, raid_bosses.raid_level, raid_bosses.pokedex_id, pokemon.pokemon_form_name != \'normal\', pokemon.pokemon_form_name, raid_bosses.pokemon_form_id +'); // Init empty keys array. $keys = []; @@ -31,74 +36,66 @@ $previous_date_end = ''; // Build the message while ($pokemon = $rs->fetch()) { - // Set current level - $current_level = $pokemon['raid_level']; - $current_date_start = $pokemon['date_start']; - $current_date_end = $pokemon['date_end']; - if($previous_date_start != $current_date_start || $previous_date_end != $current_date_end || $previous_date_start == 'FIRST_RUN') { - // Formatting. - if($previous_date_start != 'FIRST_RUN') { - $msg .= CR; - } - // Add header. - if($pokemon['scheduled'] == 0) { - $msg .= '' . getTranslation('unscheduled_bosses') . ':' . CR; - }else { - $msg .= EMOJI_CLOCK . ' ' . $pokemon['date_start'] . ' - ' . $pokemon['date_end'] . ':' . CR ; - } - $previous_level = 'FIRST_RUN'; + // Set current level + $current_level = $pokemon['raid_level']; + $current_date_start = $pokemon['date_start']; + $current_date_end = $pokemon['date_end']; + if($previous_date_start != $current_date_start || $previous_date_end != $current_date_end || $previous_date_start == 'FIRST_RUN') { + // Formatting. + if($previous_date_start != 'FIRST_RUN') { + $msg .= CR; } - // Add header for each raid level - if($previous_level != $current_level || $previous_level == 'FIRST_RUN') { - // Formatting. - if($previous_level != 'FIRST_RUN') { - $msg .= CR; - } - // Add header. - $msg .= '' . getTranslation($pokemon['raid_level'] . 'stars') . ':' . CR ; + // Add header. + if($pokemon['scheduled'] == 0) { + $msg .= '' . getTranslation('unscheduled_bosses') . ':' . CR; + }else { + $msg .= EMOJI_CLOCK . ' ' . $pokemon['date_start'] . ' — ' . $pokemon['date_end'] . ':' . CR ; } - // Add pokemon with id and name. - $poke_name = get_local_pokemon_name($pokemon['pokedex_id'], $pokemon['pokemon_form_id']); - $msg .= $poke_name . ' (#' . $pokemon['pokedex_id'] . ')' . CR; - - // Add button to edit pokemon. - if($pokemon['scheduled'] == 1) { - $keys[] = array( - 'text' => EMOJI_CLOCK . ' [' . $pokemon['raid_level'] . ']' . SP . $poke_name, - 'callback_data' => $pokemon['id'] . ':delete_scheduled_entry:0' - ); - } else { - $keys[] = array( - 'text' => '[' . $pokemon['raid_level'] . ']' . SP . $poke_name, - 'callback_data' => $pokemon['pokedex_id'] . '-' . $pokemon['pokemon_form_id'] . ':pokedex_edit_pokemon:0' - ); + $previous_level = 'FIRST_RUN'; + } + // Add header for each raid level + if($previous_level != $current_level || $previous_level == 'FIRST_RUN') { + // Formatting. + if($previous_level != 'FIRST_RUN') { + $msg .= CR; } - - // Prepare next run. - $previous_level = $current_level; - $previous_date_start = $current_date_start; - $previous_date_end = $current_date_end; + // Add header. + $msg .= '' . getTranslation($pokemon['raid_level'] . 'stars') . ':' . CR ; + } + // Add pokemon with id and name. + $poke_name = get_local_pokemon_name($pokemon['pokedex_id'], $pokemon['pokemon_form_id']); + $msg .= $poke_name . ' (#' . $pokemon['pokedex_id'] . ')' . CR; + + // Add button to edit pokemon. + if($pokemon['scheduled'] == 1) { + $keys[] = button( + EMOJI_CLOCK . ($pokemon['disabled'] == 1 ? EMOJI_DISABLED : '').' [' . $pokemon['raid_level'] . ']' . SP . $poke_name, + ['edit_scheduled_entry', 'i' => $pokemon['id']] + ); + } else { + $keys[] = button('[' . $pokemon['raid_level'] . ']' . SP . $poke_name, ['pokedex_edit_pokemon', 'p' => $pokemon['pokedex_id'] . '-' . $pokemon['pokemon_form_id']]); + } + + // Prepare next run. + $previous_level = $current_level; + $previous_date_start = $current_date_start; + $previous_date_end = $current_date_end; } if(!empty($msg)) { - // Set the message. - $msg .= CR . '' . getTranslation('pokedex_edit_pokemon') . ''; - // Set the keys. - $keys = inline_key_array($keys, 2); + // Set the message. + $msg .= CR . '' . getTranslation('pokedex_edit_pokemon') . ''; + // Set the keys. + $keys = inline_key_array($keys, 2); - // Done key. - $keys[] = [ - [ - 'text' => getTranslation('done'), - 'callback_data' => '0:exit:1' - ] - ]; + // Done key. + $keys[][] = button(getTranslation('done'), ['exit', 'd' => '1']); } else { - // Set empty keys. - $keys = []; + // Set empty keys. + $keys = []; - // Set the message. - $msg = getTranslation('pokedex_not_found'); + // Set the message. + $msg = getTranslation('pokedex_not_found'); } // Build callback message string. @@ -115,6 +112,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/pokedex_set_cp.php b/mods/pokedex_set_cp.php index 7421ee8a..b9feca5a 100644 --- a/mods/pokedex_set_cp.php +++ b/mods/pokedex_set_cp.php @@ -1,16 +1,17 @@ accessCheck('pokedex'); // Set the id. -$pokedex_id = $data['id']; +$pokedex_id = $data['p']; // Split pokedex_id and form $dex_id_form = explode('-',$pokedex_id,2); @@ -18,25 +19,19 @@ $dex_form = $dex_id_form[1]; // Get the type, level and cp -$arg = $data['arg']; -$data = explode("-", $arg); -$cp_type = $data[0]; -$cp_level = $data[1]; -$cp_value = $data[3]; +$cp_type = $data['t']; +$cp_level = $data['l']; +$cp_value = $data['cp'] ?? 0; // Set boosted string -if($cp_level == 25) { - $boosted = '_weather_cp'; -} else { - $boosted = '_cp'; -} +$boosted = ($cp_level == 25) ? '_weather_cp' : '_cp'; // Action to do: Save or add digits to cp -$action = $data[2]; +$action = $data['a']; // Get current CP values -$cp_old = get_pokemon_cp($dex_id, $dex_form); -$current_cp = $cp_old[$cp_type . $boosted]; +$pokemon = get_pokemon_info($dex_id, $dex_form); +$current_cp = $pokemon[$cp_type . $boosted]; // Log debug_log('New CP Type: ' . $cp_type); @@ -47,78 +42,51 @@ // Add digits to cp if($action == 'add') { - // Init empty keys array. - $keys = []; - - // Get the keys. - $keys = cp_keys($pokedex_id, 'pokedex_set_cp', $arg); - - // Back and abort. - $keys[] = [ - [ - 'text' => getTranslation('back'), - 'callback_data' => $pokedex_id . ':pokedex_edit_pokemon:0' - ], - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ]; - - // Build callback message string. - $callback_response = 'OK'; - - // Set the message. - $msg = getTranslation('raid_boss') . ': ' . get_local_pokemon_name($dex_id, $dex_form) . ' (#' . $dex_id . ')' . CR; - $msg .= getTranslation('pokedex_current_cp') . ' ' . $current_cp . CR . CR; - $msg .= '' .getTranslation('pokedex_' . $cp_type . $boosted) . ': ' . $cp_value . ''; + // Init empty keys array. + $keys = []; + + // Get the keys. + $keys = cp_keys($data); + + // Back and abort. + $keys[] = [ + button(getTranslation('back'), ['pokedex_edit_pokemon', 'p' => $pokedex_id]), + button(getTranslation('abort'), 'exit') + ]; + + // Build callback message string. + $callback_response = 'OK'; + + // Set the message. + $msg = getTranslation('raid_boss') . ': ' . get_local_pokemon_name($dex_id, $dex_form) . ' (#' . $dex_id . ')' . CR; + $msg .= getTranslation('pokedex_current_cp') . ' ' . $current_cp . CR . CR; + $msg .= '' .getTranslation('pokedex_' . $cp_type . $boosted) . ': ' . $cp_value . ''; // Save cp to database } else if($action == 'save') { - // Set IV level for database - if($cp_level == 25) { - $weather = 'weather_cp'; - } else { - $weather = 'cp'; - } - - // Set column name. - $cp_column = $cp_type . '_' . $weather; - - // Update cp of pokemon. - $rs = my_query( - " - UPDATE pokemon - SET $cp_column = {$cp_value} - WHERE pokedex_id = {$dex_id} - AND pokemon_form_id = '{$dex_form}' - " - ); - - // Init empty keys array. - $keys = []; - - // Back to pokemon and done keys. - $keys = [ - [ - [ - 'text' => getTranslation('back') . ' (' . get_local_pokemon_name($dex_id, $dex_form) . ')', - 'callback_data' => $pokedex_id . ':pokedex_edit_pokemon:0' - ], - [ - 'text' => getTranslation('done'), - 'callback_data' => '0:exit:1' - ] - ] - ]; - - // Build callback message string. - $callback_response = getTranslation('pokemon_saved') . ' ' . get_local_pokemon_name($pokedex_id); - - // Set the message. - $msg = getTranslation('pokemon_saved') . CR; - $msg .= '' . get_local_pokemon_name($dex_id, $dex_form) . ' (#' . $dex_id . ')' . CR . CR; - $msg .= getTranslation('pokedex_' . $cp_type . $boosted) . ': ' . $cp_value . ''; + // Set column name. + $cp_column = $cp_type . $boosted; + + // Update cp of pokemon. + $rs = my_query(' + UPDATE pokemon + SET ' . $cp_column . ' = ? + WHERE pokedex_id = ? + AND pokemon_form_id = ? + ', [$cp_value, $dex_id, $dex_form] + ); + + // Back to pokemon and done keys. + $keys[0][0] = button(getTranslation('back') . ' (' . get_local_pokemon_name($dex_id, $dex_form) . ')', ['pokedex_edit_pokemon', 'p' => $pokedex_id]); + $keys[0][1] = button(getTranslation('done') . ' (' . get_local_pokemon_name($dex_id, $dex_form) . ')', ['exit', 'd' => '1']); + + // Build callback message string. + $callback_response = getTranslation('pokemon_saved') . ' ' . get_local_pokemon_name($dex_id, $dex_form); + + // Set the message. + $msg = getTranslation('pokemon_saved') . CR; + $msg .= '' . get_local_pokemon_name($dex_id, $dex_form) . ' (#' . $dex_id . ')' . CR . CR; + $msg .= getTranslation('pokedex_' . $cp_type . $boosted) . ': ' . $cp_value . ''; } // Telegram JSON array. @@ -133,5 +101,53 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); -// Exit. -exit(); +function cp_keys($data) +{ + // Get the type, level and cp + $old_cp = $data['cp'] ?? ''; + + // Save and reset values + $saveData = $resetData = $data; + $saveData['a'] = 'save'; + unset($resetData['cp']); + + // Init empty keys array. + $keys = []; + + // Max CP is 9999 and no the value 999 is not a typo! + // Keys will be shown up to 999 and when user is adding one more number we exceed 999, so we remove the keys then + // This means we do not exceed a Max CP of 9999 :) + if($old_cp <= 999) { + $buttonData = $data; + // Add keys 0 to 9 + /** + * 7 8 9 + * 4 5 6 + * 1 2 3 + * 0 + */ + + // 7 8 9 + foreach ([7, 8, 9, 4, 5, 6, 1, 2, 3] as $i) { + // Set new cp + $buttonData['cp'] = $old_cp . $i; + // Set keys. + $keys[] = button($i, $buttonData); + } + + // 0 + $buttonData['cp'] = $old_cp . '0'; + $keys[] = button('0', $buttonData); + } + + // Save + $keys[] = button(EMOJI_DISK, $saveData); + + // Reset + $keys[] = button(getTranslation('reset'), $resetData); + + // Get the inline key array. + $keys = inline_key_array($keys, 3); + + return $keys; +} diff --git a/mods/pokedex_set_raid_level.php b/mods/pokedex_set_raid_level.php index 4812bc4c..e9350057 100644 --- a/mods/pokedex_set_raid_level.php +++ b/mods/pokedex_set_raid_level.php @@ -1,107 +1,85 @@ accessCheck('pokedex'); // Set the id. -$pokedex_id = $data['id']; +$pokedex_id = $data['p']; // Get the raid level. -$arg = $data['arg']; +$newLevel = $data['l'] ?? false; // Split pokedex_id and form $dex_id_form = explode('-',$pokedex_id,2); $dex_id = $dex_id_form[0]; $dex_form = $dex_id_form[1]; +$pokemon = get_pokemon_info($dex_id, $dex_form); + // Set raid level or show raid levels? -if($data['arg'] == "setlevel") { - $raid_levels = [0,1,3,5,6,'X']; - - // Init empty keys array. - $keys = []; - - // Create keys array. - foreach($raid_levels as $lv) { - $keys[] = [ - array( - 'text' => getTranslation($lv . 'stars'), - 'callback_data' => $pokedex_id . ':pokedex_set_raid_level:' . $lv - ) - ]; - } - - // Back and abort. - $keys[] = [ - [ - 'text' => getTranslation('back'), - 'callback_data' => $pokedex_id . ':pokedex_edit_pokemon:0' - ], - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ]; - - // Build callback message string. - $callback_response = getTranslation('select_raid_level'); - - // Set the message. - $msg = getTranslation('raid_boss') . ': ' . get_local_pokemon_name($dex_id, $dex_form) . ' (#' . $dex_id . ')' . CR; - $old_raid_level = get_raid_level($dex_id, $dex_form); - $msg .= getTranslation('pokedex_current_raid_level') . ' ' . getTranslation($old_raid_level . 'stars') . CR . CR; - $msg .= '' . getTranslation('pokedex_new_raid_level') . ':'; +if($newLevel === false) { + $raid_levels = array_merge([0], RAID_LEVEL_ALL); + + // Init empty keys array. + $keys = []; + + // Create keys array. + foreach($raid_levels as $lv) { + $keys[][] = button(getTranslation($lv . 'stars'), ['pokedex_set_raid_level', 'p' => $pokedex_id, 'l' => $lv]); + } + + // Back and abort. + $keys[] = [ + button(getTranslation('back'), ['pokedex_edit_pokemon', 'p' => $pokedex_id]), + button(getTranslation('abort'), 'exit') + ]; + + // Build callback message string. + $callback_response = getTranslation('select_raid_level'); + + // Set the message. + $msg = getTranslation('raid_boss') . ': ' . get_local_pokemon_name($dex_id, $dex_form) . ' (#' . $dex_id . ')' . CR; + $msg .= getTranslation('pokedex_current_raid_level') . ' ' . getTranslation($pokemon['raid_level'] . 'stars') . CR . CR; + $msg .= '' . getTranslation('pokedex_new_raid_level') . ':'; } else { - // Update raid level of pokemon. - if($arg == 0 && get_raid_level($dex_id, $dex_form) != 0) { - $rs = my_query( - " - DELETE FROM raid_bosses - WHERE pokedex_id = '{$dex_id}' - AND pokemon_form_id = '{$dex_form}' - AND scheduled = 0 - " - ); - }else { - $rs = my_query( - " - INSERT INTO raid_bosses (pokedex_id, pokemon_form_id, raid_level) - VALUES ('{$dex_id}', '{$dex_form}', '{$arg}') - " - ); - } - - // Init empty keys array. - $keys = []; - - // Back to pokemon and done keys. - $keys = [ - [ - [ - 'text' => getTranslation('back') . ' (' . get_local_pokemon_name($dex_id, $dex_form) . ')', - 'callback_data' => $pokedex_id . ':pokedex_edit_pokemon:0' - ], - [ - 'text' => getTranslation('done'), - 'callback_data' => '0:exit:1' - ] - ] - ]; - - // Build callback message string. - $callback_response = getTranslation('pokemon_saved') . ' ' . get_local_pokemon_name($dex_id, $dex_form); - - // Set the message. - $msg = getTranslation('pokemon_saved') . CR; - $msg .= '' . get_local_pokemon_name($dex_id, $dex_form) . ' (#' . $dex_id . ')' . CR . CR; - $msg .= getTranslation('pokedex_new_raid_level') . ':' . CR; - $msg .= '' . getTranslation($arg . 'stars') . ''; + // Update raid level of pokemon. + my_query(' + DELETE FROM raid_bosses + WHERE pokedex_id = ? + AND pokemon_form_id = ? + AND scheduled = 0 + ', [$dex_id, $dex_form] + ); + if($newLevel != 0) { + my_query(' + INSERT INTO raid_bosses (pokedex_id, pokemon_form_id, raid_level) + VALUES (?, ?, ?) + ', + [$dex_id, $dex_form, $newLevel] + ); + } + + // Back to pokemon and done keys. + $keys[] = [ + button(getTranslation('back') . ' (' . get_local_pokemon_name($dex_id, $dex_form) . ')', ['pokedex_edit_pokemon', 'p' => $pokedex_id]), + button(getTranslation('done'), ['exit', 'd' => '1']) + ]; + + // Build callback message string. + $callback_response = getTranslation('pokemon_saved') . ' ' . get_local_pokemon_name($dex_id, $dex_form); + + // Set the message. + $msg = getTranslation('pokemon_saved') . CR; + $msg .= '' . get_local_pokemon_name($dex_id, $dex_form) . ' (#' . $dex_id . ')' . CR . CR; + $msg .= getTranslation('pokedex_new_raid_level') . ':' . CR; + $msg .= '' . getTranslation($newLevel . 'stars') . ''; } // Telegram JSON array. @@ -115,6 +93,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/pokedex_set_shiny.php b/mods/pokedex_set_shiny.php index f3a74f4d..adaeb604 100644 --- a/mods/pokedex_set_shiny.php +++ b/mods/pokedex_set_shiny.php @@ -1,19 +1,20 @@ accessCheck('pokedex'); // Set the id. -$pokedex_id = $data['id']; +$pokedex_id = $data['p']; // Get the shiny status. -$arg = $data['arg']; +$arg = $data['s'] ?? 'setshiny'; // Split pokedex_id and form $dex_id_form = explode('-',$pokedex_id,2); @@ -21,90 +22,48 @@ $dex_form = $dex_id_form[1]; // Set shiny or ask to set? -if($data['arg'] == "setshiny") { - // Get current shiny status from database. - $shiny = get_pokemon_shiny_status($dex_id, $dex_form); - - // Init empty keys array. - $keys = []; - - // Create keys array. - if($shiny == 0) { - // Enable shiny. - $old_shiny_status = getTranslation('not_shiny'); - $keys[] = [ - array( - 'text' => getTranslation('shiny'), - 'callback_data' => $pokedex_id . ':pokedex_set_shiny:1' - ) - ]; - - } else { - // Disable shiny. - $old_shiny_status = getTranslation('shiny'); - $keys[] = [ - array( - 'text' => getTranslation('not_shiny'), - 'callback_data' => $pokedex_id . ':pokedex_set_shiny:0' - ) - ]; - } - - // Back and abort. - $keys[] = [ - [ - 'text' => getTranslation('back'), - 'callback_data' => $pokedex_id . ':pokedex_edit_pokemon:0' - ], - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ]; - - // Build callback message string. - $callback_response = getTranslation('select_shiny_status'); - - // Set the message. - $msg = getTranslation('raid_boss') . ': ' . get_local_pokemon_name($dex_id, $dex_form) . ' (#' . $dex_id . ')' . CR; - $msg .= getTranslation('pokedex_current_status') . SP . $old_shiny_status . CR . CR; - $msg .= '' . getTranslation('pokedex_new_status') . ':'; +if($arg == 'setshiny') { + // Get current shiny status from database. + $pokemon = get_pokemon_info($dex_id, $dex_form); + + $shinyText = ($pokemon['shiny'] == 0) ? 'shiny' : 'not_shiny'; + $old_shiny_status = ($pokemon['shiny'] == 0) ? getTranslation('not_shiny') : EMOJI_SHINY . SP . getTranslation('shiny'); + $newShinyValue = ($pokemon['shiny'] == 0) ? 1 : 0; + + // Back and abort. + $keys[][] = button(getTranslation($shinyText), ['pokedex_set_shiny', 'p' => $pokedex_id, 's' => $newShinyValue]); + $keys[][] = button(getTranslation('back'), ['pokedex_edit_pokemon', 'p' => $pokedex_id]); + $keys[][] = button(getTranslation('abort'), 'exit'); + + // Build callback message string. + $callback_response = getTranslation('select_shiny_status'); + + // Set the message. + $msg = getTranslation('raid_boss') . ': ' . get_local_pokemon_name($dex_id, $dex_form) . ' (#' . $dex_id . ')' . CR; + $msg .= getTranslation('pokedex_current_status') . SP . $old_shiny_status . CR . CR; + $msg .= '' . getTranslation('pokedex_new_status') . ':'; } else { - // Update shiny status of pokemon. - $rs = my_query( - " - UPDATE pokemon - SET shiny = '{$arg}' - WHERE pokedex_id = {$dex_id} - AND pokemon_form_id = '{$dex_form}' - " - ); - - // Init empty keys array. - $keys = []; - - // Back to pokemon and done keys. - $keys = [ - [ - [ - 'text' => getTranslation('back') . ' (' . get_local_pokemon_name($dex_id, $dex_form) . ')', - 'callback_data' => $pokedex_id . ':pokedex_edit_pokemon:0' - ], - [ - 'text' => getTranslation('done'), - 'callback_data' => '0:exit:1' - ] - ] - ]; - - // Build callback message string. - $callback_response = getTranslation('pokemon_saved') . ' ' . get_local_pokemon_name($dex_id, $dex_form); - - // Set the message. - $msg = getTranslation('pokemon_saved') . CR; - $msg .= '' . get_local_pokemon_name($dex_id, $dex_form) . ' (#' . $dex_id . ')' . CR . CR; - $msg .= getTranslation('pokedex_new_status') . ':' . CR; - $msg .= '' . (($arg == 1) ? (getTranslation('shiny')) : (getTranslation('not_shiny'))) . ''; + // Update shiny status of pokemon. + $rs = my_query(' + UPDATE pokemon + SET shiny = ? + WHERE pokedex_id = ? + AND pokemon_form_id = ? + ', [$arg, $dex_id, $dex_form] + ); + + // Back to pokemon and done keys. + $keys[0][0] = button(getTranslation('back') . ' (' . get_local_pokemon_name($dex_id, $dex_form) . ')', ['pokedex_edit_pokemon', 'p' => $pokedex_id]); + $keys[0][1] = button(getTranslation('done'), ['exit', 'd' => '1']); + + // Build callback message string. + $callback_response = getTranslation('pokemon_saved') . ' ' . get_local_pokemon_name($dex_id, $dex_form); + + // Set the message. + $msg = getTranslation('pokemon_saved') . CR; + $msg .= '' . get_local_pokemon_name($dex_id, $dex_form) . ' (#' . $dex_id . ')' . CR . CR; + $msg .= getTranslation('pokedex_new_status') . ':' . CR; + $msg .= '' . (($arg == 1) ? (getTranslation('shiny')) : (getTranslation('not_shiny'))) . ''; } // Telegram JSON array. @@ -118,6 +77,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/pokedex_set_weather.php b/mods/pokedex_set_weather.php index d671aa4e..dc803d60 100644 --- a/mods/pokedex_set_weather.php +++ b/mods/pokedex_set_weather.php @@ -1,16 +1,19 @@ accessCheck('pokedex'); // Set the id. -$pokedex_id = $data['id']; +$pokedex_id = $data['p']; // Split pokedex_id and form $dex_id_form = explode('-',$pokedex_id,2); @@ -18,82 +21,62 @@ $dex_form = $dex_id_form[1]; // Get the action, old and new weather -$arg = $data['arg']; -$data = explode("-", $arg); -$action = $data[0]; -$new_weather = $data[1]; -$old_weather = get_pokemon_weather($dex_id, $dex_form); +$action = $data['a'] ?? 'add'; +$new_weather = $data['w'] ?? 0; + +$pokemon = get_pokemon_info($dex_id, $dex_form); +$old_weather = $pokemon['weather']; // Log debug_log('Action: ' . $action); debug_log('Old weather: ' . $old_weather); debug_log('New weather: ' . $new_weather); +// Init empty keys array. +$keys = []; // Add weather if($action == 'add') { - // Init empty keys array. - $keys = []; - - // Get the keys. - $keys = weather_keys($pokedex_id, 'pokedex_set_weather', $arg); - - // Build callback message string. - $callback_response = 'OK'; - - // Back and abort. - $keys[] = [ - [ - 'text' => getTranslation('back'), - 'callback_data' => $pokedex_id . ':pokedex_edit_pokemon:0' - ], - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ]; - - // Set the message. - $msg = getTranslation('raid_boss') . ': ' . get_local_pokemon_name($dex_id, $dex_form) . ' (#' . $dex_id . ')' . CR; - $msg .= getTranslation('pokedex_current_weather') . ' ' . get_weather_icons($old_weather) . CR . CR; - $msg .= '' . getTranslation('pokedex_new_weather') . ' ' . get_weather_icons($new_weather); + + // Get the keys. + $keys = weather_keys($data); + + // Build callback message string. + $callback_response = 'OK'; + + // Back and abort. + $keys[] = [ + button(getTranslation('back'), ['pokedex_edit_pokemon', 'p' => $pokedex_id]), + button(getTranslation('abort'), 'exit') + ]; + + // Set the message. + $msg = getTranslation('raid_boss') . ': ' . get_local_pokemon_name($dex_id, $dex_form) . ' (#' . $dex_id . ')' . CR; + $msg .= getTranslation('pokedex_current_weather') . ' ' . get_weather_icons($old_weather) . CR . CR; + $msg .= '' . getTranslation('pokedex_new_weather') . ' ' . get_weather_icons($new_weather); // Save weather to database } else if($action == 'save') { - // Update weather of pokemon. - $rs = my_query( - " - UPDATE pokemon - SET weather = {$new_weather} - WHERE pokedex_id = {$dex_id} - AND pokemon_form_id = '{$dex_form}' - " - ); - - // Init empty keys array. - $keys = []; - - // Back to pokemon and done keys. - $keys = [ - [ - [ - 'text' => getTranslation('back') . ' (' . get_local_pokemon_name($dex_id, $dex_form) . ')', - 'callback_data' => $pokedex_id . ':pokedex_edit_pokemon:0' - ], - [ - 'text' => getTranslation('done'), - 'callback_data' => '0:exit:1' - ] - ] - ]; - - // Build callback message string. - $callback_response = getTranslation('pokemon_saved') . ' ' . get_local_pokemon_name($dex_id, $dex_form); - - // Set the message. - $msg = getTranslation('pokemon_saved') . CR; - $msg .= '' . get_local_pokemon_name($dex_id, $dex_form) . ' (#' . $dex_id . ')' . CR . CR; - $msg .= getTranslation('pokedex_weather') . ':' . CR; - $msg .= '' . get_weather_icons($new_weather) . ''; + // Update weather of pokemon. + $rs = my_query(' + UPDATE pokemon + SET weather = ? + WHERE pokedex_id = ? + AND pokemon_form_id = ? + ', [$new_weather, $dex_id, $dex_form] + ); + + // Back to pokemon and done keys. + $keys[0][0] = button(getTranslation('back') . ' (' . get_local_pokemon_name($dex_id, $dex_form) . ')', ['pokedex_edit_pokemon', 'p' => $pokedex_id]); + $keys[0][1] = button(getTranslation('abort'), ['exit', 'd' => '1']); + + // Build callback message string. + $callback_response = getTranslation('pokemon_saved') . ' ' . get_local_pokemon_name($dex_id, $dex_form); + + // Set the message. + $msg = getTranslation('pokemon_saved') . CR; + $msg .= '' . get_local_pokemon_name($dex_id, $dex_form) . ' (#' . $dex_id . ')' . CR . CR; + $msg .= getTranslation('pokedex_weather') . ':' . CR; + $msg .= '' . get_weather_icons($new_weather) . ''; } // Telegram JSON array. @@ -107,6 +90,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/post_raid.php b/mods/post_raid.php index a165074d..6de616c8 100644 --- a/mods/post_raid.php +++ b/mods/post_raid.php @@ -14,10 +14,7 @@ $chat = $data['arg']; require_once(LOGIC_PATH . '/send_raid_poll.php'); -$tg_json = send_raid_poll($id, $chat); +$tg_json = send_raid_poll($id, [create_chat_object([$chat])]); // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/raid_by_gym.php b/mods/raid_by_gym.php deleted file mode 100644 index 58916693..00000000 --- a/mods/raid_by_gym.php +++ /dev/null @@ -1,62 +0,0 @@ - 1) ? $args[1] : false; - -// Back key id, action and arg -$back_id = $gymarea_id; -$back_action = 'raid_by_gym_letter'; -$back_arg = 0; - -// Get the keys. -$keys = raid_edit_gym_keys($first, $gymarea_id); - -// No keys found. -if (!$keys) { - // Create the keys. - $keys = [ - [ - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ] - ]; -} else { - // Add navigation keys. - $nav_keys = []; - $nav_keys[] = universal_inner_key($nav_keys, $back_id, $back_action, $back_arg, getTranslation('back')); - $nav_keys[] = universal_inner_key($nav_keys, '0', 'exit', '0', getTranslation('abort')); - $nav_keys = inline_key_array($nav_keys, 2); - // Merge keys. - $keys = array_merge($keys, $nav_keys); -} - -// Build callback message string. -$callback_response = getTranslation('here_we_go'); - -// Telegram JSON array. -$tg_json = array(); - -// Answer callback. -$tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_response, true); - -// Edit the message. -$tg_json[] = edit_message($update, getTranslation('select_gym_name'), $keys, false, true); - -// Telegram multicurl request. -curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/raid_by_gym_letter.php b/mods/raid_by_gym_letter.php deleted file mode 100644 index a8be56b7..00000000 --- a/mods/raid_by_gym_letter.php +++ /dev/null @@ -1,83 +0,0 @@ - getTranslation('not_supported'), - 'callback_data' => '0:exit:0' - ] - ] - ]; -} - -// Build callback message string. -$callback_response = getTranslation('select_gym'); - -// Telegram JSON array. -$tg_json = array(); - -// Answer callback. -$tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_response, true); - -$msg = ''; -// Edit the message. -if($config->ENABLE_GYM_AREAS) { - if($keys_and_gymarea['gymarea_name'] == '') { - $msg .= '' . getTranslation('select_gym_area') . '' . CR; - }elseif($config->DEFAULT_GYM_AREA !== false) { - if($keys_and_gymarea['letters']) { - $msg .= '' . getTranslation('select_gym_first_letter_or_gym_area') . '' . CR; - }else { - $msg .= '' . getTranslation('select_gym_name_or_gym_area') . '' . CR; - } - }else { - if($keys_and_gymarea['letters']) { - $msg .= '' . getTranslation('select_gym_first_letter') . '' . CR; - }else { - $msg .= '' . getTranslation('select_gym_name') . '' . CR; - } - } -}elseif($keys_and_gymarea['letters']) { - $msg .= '' . getTranslation('select_gym_first_letter') . '' . CR; -}else { - $msg .= '' . getTranslation('select_gym_name') . '' . CR; -} -$msg.= (($keys_and_gymarea['gymarea_name'] != '') ? CR . CR . getTranslation('current_gymarea') . ': ' . $keys_and_gymarea['gymarea_name'] : ''); -$tg_json[] = edit_message($update, $msg, $keys, false, true); - -// Telegram multicurl request. -curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/raid_by_location.php b/mods/raid_by_location.php index a7ddf3df..f0761373 100644 --- a/mods/raid_by_location.php +++ b/mods/raid_by_location.php @@ -1,32 +1,35 @@ accessCheck('create'); // Enabled? if(!$config->RAID_VIA_LOCATION) { - debug_log('Creating raids by sharing a location is not enabled in config! Exiting!'); - send_message($update['message']['chat']['id'], '' . getTranslation('bot_access_denied') . ''); - exit(); + debug_log('Creating raids by sharing a location is not enabled in config! Exiting!'); + send_message(create_chat_object([$update['message']['chat']['id']]), '' . getTranslation('bot_access_denied') . ''); + exit(); } $reg_exp_coordinates = '^[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?),\s*[-+]?(180(\.0+)?|((1[0-7]\d)|([1-9]?\d))(\.\d+)?)$^'; // Get latitude / longitude values from Telegram if(isset($update['message']['location']) && preg_match($reg_exp_coordinates, $update['message']['location']['latitude'] . ',' . $update['message']['location']['longitude'])) { - $lat = $update['message']['location']['latitude']; - $lon = $update['message']['location']['longitude']; + $lat = $update['message']['location']['latitude']; + $lon = $update['message']['location']['longitude']; } else if(isset($update['callback_query']) && preg_match($reg_exp_coordinates, $data['id'] . ',' . $data['arg'])) { - $lat = $data['id']; - $lon = $data['arg']; + $lat = $data['id']; + $lon = $data['arg']; } else { - send_message($update['message']['chat']['id'], '' . getTranslation('invalid_input') . ''); - exit(); + send_message(create_chat_object([$update['message']['chat']['id']]), '' . getTranslation('invalid_input') . ''); + exit(); } // Debug @@ -39,103 +42,96 @@ // Temporary gym_name if($config->RAID_VIA_LOCATION_FUNCTION == 'remote') { - $gym_name = getPublicTranslation('remote_raid') . ': '.$addr['district']; - $gym = false; - $gym_letter = substr($gym_name, 0, 1); + $gym_name = getPublicTranslation('remote_raid') . ': '.$addr['district']; + $gym = false; + $gym_letter = substr($gym_name, 0, 1); }else { - $gym_name = '#' . $update['message']['chat']['id']; - $gym_letter = substr($gym_name, 0, 1); - // Get gym by temporary name. - $gym = get_gym_by_telegram_id($gym_name); + $gym_name = '#' . $update['message']['chat']['id']; + $gym_letter = substr($gym_name, 0, 1); + // Get gym by temporary name. + $gym = get_gym_by_telegram_id($gym_name); } // If gym is already in the database, make sure no raid is active before continuing! if($gym) { - debug_log('Gym found in the database! Checking for active raid now!'); - $gym_id = $gym['id']; + debug_log('Gym found in the database! Checking for active raid now!'); + $gym_id = $gym['id']; - // Check for duplicate raid - $duplicate_id = 0; - $duplicate_id = active_raid_duplication_check($gym_id); + // Check for duplicate raid + $duplicate_id = 0; + $duplicate_id = active_raid_duplication_check($gym_id); - // Continue with raid creation - if($duplicate_id > 0) { - debug_log('Active raid is in progress!'); - debug_log('Tell user to update the gymname and exit!'); + // Continue with raid creation + if($duplicate_id > 0) { + debug_log('Active raid is in progress!'); + debug_log('Tell user to update the gymname and exit!'); - // Show message that a raid is active on that gym. - $raid_id = $duplicate_id; - $raid = get_raid($raid_id); + // Show message that a raid is active on that gym. + $raid_id = $duplicate_id; + $raid = get_raid($raid_id); - // Build message. - $msg = EMOJI_WARN . SP . getTranslation('raid_already_exists') . SP . EMOJI_WARN . CR . show_raid_poll_small($raid); + // Build message. + $msg = EMOJI_WARN . SP . getTranslation('raid_already_exists') . SP . EMOJI_WARN . CR . show_raid_poll_small($raid); - // Tell user to update the gymname first to create another raid by location - $msg .= getTranslation('gymname_then_location'); - $keys = []; + // Tell user to update the gymname first to create another raid by location + $msg .= getTranslation('gymname_then_location'); + $keys = []; - // Send message. - send_message($update['message']['chat']['id'], $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); + // Send message. + send_message(create_chat_object([$update['message']['chat']['id']]), $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); - exit(); - } else { - debug_log('No active raid found! Continuing now ...'); - } + exit(); + } else { + debug_log('No active raid found! Continuing now ...'); + } } else { - // Set gym_id to 0 - $gym_id = 0; - debug_log('No gym found in the database! Continuing now ...'); + // Set gym_id to 0 + $gym_id = 0; + debug_log('No gym found in the database! Continuing now ...'); } // Insert / update gym. -try { - - global $dbh; - - // Build query to check if gym is already in database or not - $rs = my_query(" - SELECT COUNT(*) AS count - FROM gyms - WHERE gym_name = '{$gym_name}' - "); - - $row = $rs->fetch(); - $parameters = [ 'gym_name' => $gym_name, - 'lat' => $lat, - 'lon' => $lon, - 'address' => $address, - ]; - // Gym already in database or new - if (empty($row['count']) or $config->RAID_VIA_LOCATION_FUNCTION == 'remote') { - // insert gym in table. - debug_log('Gym not found in database gym list! Inserting gym "' . $gym_name . '" now.'); - $parameters['img_url'] = 'file://' . IMAGES_PATH . '/gym_default.png'; - $query = ' - INSERT INTO gyms (gym_name, lat, lon, address, show_gym, img_url, temporary_gym) - VALUES (:gym_name, :lat, :lon, :address, 0, :img_url, 1) - '; - } else { - // Update gyms table to reflect gym changes. - debug_log('Gym found in database gym list! Updating gym "' . $gym_name . '" now.'); - $query = ' - UPDATE gyms - SET lat = :lat, - lon = :lon, - address = :address - WHERE gym_name = :gym_name - '; - } - $statement = $dbh->prepare($query); - $statement->execute($parameters); - // Get gym id from insert. - if($gym_id == 0) { - $gym_id = $dbh->lastInsertId(); - } -} catch (PDOException $exception) { - - error_log($exception->getMessage()); - $dbh = null; - exit(); + +// Build query to check if gym is already in database or not +$rs = my_query(' + SELECT COUNT(*) AS count + FROM gyms + WHERE gym_name = ? + ',[$gym_name] +); + +$row = $rs->fetch(); +$parameters = [ + 'gym_name' => $gym_name, + 'lat' => $lat, + 'lon' => $lon, + 'address' => $address, +]; +// Gym already in database or new +if (empty($row['count']) or $config->RAID_VIA_LOCATION_FUNCTION == 'remote') { + // insert gym in table. + debug_log('Gym not found in database gym list! Inserting gym "' . $gym_name . '" now.'); + $parameters['img_url'] = 'file://' . IMAGES_PATH . '/gym_default.png'; + $parameters['temp'] = ($config->RAID_VIA_LOCATION_FUNCTION == 'remote') ? 1 : 0; + $query = ' + INSERT INTO gyms (gym_name, lat, lon, address, show_gym, img_url, temporary_gym) + VALUES (:gym_name, :lat, :lon, :address, 0, :img_url, :temp) + '; +} else { + // Update gyms table to reflect gym changes. + debug_log('Gym found in database gym list! Updating gym "' . $gym_name . '" now.'); + $query = ' + UPDATE gyms + SET lat = :lat, + lon = :lon, + address = :address + WHERE gym_name = :gym_name + '; +} +$statement = my_query($query, $parameters); +// Get gym id from insert. +if($gym_id == 0) { + $gym_id = $dbh->lastInsertId(); } // Write to log. @@ -143,46 +139,31 @@ debug_log('Gym Name: ' . $gym_name); // Create the keys. -$keys = [ - [ - [ - 'text' => getTranslation('next'), - 'callback_data' => $gym_letter . ':edit_raidlevel:' . $gym_id - ] - ], - [ - [ - 'text' => getTranslation('abort'), - 'callback_data' => $gym_id . ':exit:2' - ] - ] -]; +$keys[][] = button(getTranslation('next'), ['edit_raidlevel', 'gl' => $gym_letter, 'g' => $gym_id, 'z' => 1]); +$keys[][] = button(getTranslation('abort'), 'exit'); // Answer location message. if(isset($update['message']['location'])) { - // Build message. - $msg = getTranslation('create_raid') . ': ' . $address . ''; + // Build message. + $msg = getTranslation('create_raid') . ': ' . $address . ''; - // Send message. - send_message($update['message']['chat']['id'], $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); + // Send message. + send_message(create_chat_object([$update['message']['chat']['id']]), $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); // Answer forwarded location message from geo_create. } else if(isset($update['callback_query'])) { - // Build callback message string. - $callback_response = getTranslation('here_we_go'); + // Build callback message string. + $callback_response = getTranslation('here_we_go'); - // Telegram JSON array. - $tg_json = array(); + // Telegram JSON array. + $tg_json = array(); - // Answer callback. - $tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_response, true); + // Answer callback. + $tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_response, true); - // Edit the message. - $tg_json[] = edit_message($update, getTranslation('select_gym_name'), $keys, false, true); + // Edit the message. + $tg_json[] = edit_message($update, getTranslation('select_gym_name'), $keys, false, true); - // Telegram multicurl request. - curl_json_multi_request($tg_json); + // Telegram multicurl request. + curl_json_multi_request($tg_json); } - -// Exit. -exit(); diff --git a/mods/raid_edit_poke.php b/mods/raid_edit_poke.php index 546f1833..83b3c2af 100644 --- a/mods/raid_edit_poke.php +++ b/mods/raid_edit_poke.php @@ -1,4 +1,5 @@ raidaccessCheck($raid_id, 'pokemon'); // Get raid level -$raid_level = $data['arg']; +$raid_level = $data['rl']; debug_log('Raid level of pokemon: ' . $raid_level); // Level found if ($raid_level != '0') { - // Get the keys. - $keys = pokemon_keys($raid_id, $raid_level, 'raid_set_poke'); - - // Add navigation keys. - $nav_keys = []; - $nav_keys[] = universal_inner_key($nav_keys, '0', 'exit', '0', getTranslation('abort')); - $nav_keys = inline_key_array($nav_keys, 1); + // Get the keys. + $keys = pokemon_keys($data, $raid_level, 'raid_set_poke'); - // Merge keys. - $keys = array_merge($keys, $nav_keys); + $keys[][] = button(getTranslation('abort'), 'exit'); } else { - // Create the keys. - $keys = [ - [ - [ - 'text' => getTranslation('not_supported'), - 'callback_data' => '0:exit:0' - ] - ] - ]; + // Create the keys. + $keys[][] = button(getTranslation('not_supported'), 'exit'); } // Build callback message string. @@ -55,13 +43,10 @@ $msg = getTranslation('raid_boss') . ':' . SP . get_local_pokemon_name($raid['pokemon'],$raid['pokemon_form']) . CR . CR; $msg .= '' . getTranslation('select_raid_boss') . ':'; if (isset($update['callback_query']['inline_message_id'])) { - $tg_json[] = editMessageText($update['callback_query']['inline_message_id'], $msg, $keys, NULL, false, true); + $tg_json[] = editMessageText($update['callback_query']['inline_message_id'], $msg, $keys, NULL, false, true); } else { - $tg_json[] = editMessageText($update['callback_query']['message']['message_id'], $msg, $keys, $update['callback_query']['message']['chat']['id'], $keys, true); + $tg_json[] = editMessageText($update['callback_query']['message']['message_id'], $msg, $keys, $update['callback_query']['message']['chat']['id'], $keys, true); } // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/raid_set_poke.php b/mods/raid_set_poke.php index 0bd7f9ee..2d880362 100644 --- a/mods/raid_set_poke.php +++ b/mods/raid_set_poke.php @@ -1,30 +1,33 @@ raidaccessCheck($raidId, 'pokemon'); -// Set the id. -$id = $data['id']; -$pokemon_id_form = get_pokemon_by_table_id($data['arg']); +$pokemon_id_form = get_pokemon_by_table_id($data['p']); // Update pokemon in the raid table. -my_query( - " - UPDATE raids - SET pokemon = '{$pokemon_id_form['pokedex_id']}', - pokemon_form = '{$pokemon_id_form['pokemon_form_id']}' - WHERE id = {$id} - " +my_query(' + UPDATE raids + SET pokemon = ?, + pokemon_form = ? + WHERE id = ? + ',[$pokemon_id_form['pokedex_id'], $pokemon_id_form['pokemon_form_id'], $raidId] ); // Get raid times. -$raid = get_raid($data['id']); +$raid = get_raid($raidId); // Create the keys. $keys = []; @@ -48,13 +51,10 @@ // Update the shared raid polls. require_once(LOGIC_PATH .'/update_raid_poll.php'); -$tg_json = update_raid_poll($id, $raid, false, $tg_json, false); +$tg_json = update_raid_poll($raidId, $raid, false, $tg_json, true); // Alert users. $tg_json = alarm($raid, $update['callback_query']['from']['id'], 'new_boss', '', $tg_json); // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/raid_share.php b/mods/raid_share.php index d8bd51be..5588cf2b 100644 --- a/mods/raid_share.php +++ b/mods/raid_share.php @@ -7,16 +7,17 @@ //debug_log($update); //debug_log($data); -// Access check. -raid_access_check($update, $data, 'share'); - // Get raid id. -$id = $data['id']; +$raidId = $data['r']; -// Get chat id. -$chat = $data['arg']; +// Access check. +$botUser->raidaccessCheck($raidId, 'share'); -$tg_json = send_raid_poll($id, $chat); +// Get chat id. +$chat = $data['c']; +$thread = $data['t'] ?? ''; +$chatObj = [['id' => $chat, 'thread' => $thread]]; +$tg_json = send_raid_poll($raidId, $chatObj); // Set callback keys and message $callback_msg = getTranslation('successfully_shared'); @@ -30,7 +31,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -$dbh = null; -exit(); diff --git a/mods/raids_delete.php b/mods/raids_delete.php index a6d377f2..e422c280 100644 --- a/mods/raids_delete.php +++ b/mods/raids_delete.php @@ -1,68 +1,105 @@ Confirmation required // 1 -> Cancel deletion // 2 -> Execute deletion -$action = $data['arg']; +$action = $data['a'] ?? 0; // Get the raid id. -$id = $data['id']; +$raidId = $data['r']; + +// Access check. +$botUser->raidaccessCheck($raidId, 'delete'); // Execute the action. if ($action == 0) { - // Get raid. - $raid = get_raid($id); - - // Write to log. - debug_log('Asking for confirmation to delete the following raid:'); - debug_log($raid); - - // Create keys array. - $keys = [ - [ - [ - 'text' => getTranslation('yes'), - 'callback_data' => $raid['id'] . ':raids_delete:2' - ], - [ - 'text' => getTranslation('no'), - 'callback_data' => $raid['id'] . ':raids_delete:1' - ] - ] - ]; - - // Set message. - $msg = EMOJI_WARN . ' ' . getTranslation('delete_this_raid') . ' ' . EMOJI_WARN . CR . CR; - $msg .= show_raid_poll_small($raid); -} else if ($action == 1) { - debug_log('Raid deletion for ' . $id . ' was canceled!'); - // Set message. - $msg = '' . getTranslation('raid_deletion_was_canceled') . ''; + // Get raid. + $raid = get_raid($raidId); - // Set keys. - $keys = []; -} else if ($action == 2) { - debug_log('Confirmation to delete raid ' . $id . ' was received!'); - // Set message. - $msg = getTranslation('raid_successfully_deleted'); + // Write to log. + debug_log('Asking for confirmation to delete the following raid:'); + debug_log($raid); - // Set keys. - $keys = []; + // Create keys array. + $keys[0][0] = button(getTranslation('yes'), ['raids_delete', 'r' => $raid['id'], 'a' => 2]); + $keys[0][1] = button(getTranslation('no'), ['raids_delete', 'r' => $raid['id'], 'a' => 1]); - // Delete raid from database. - delete_raid($id); + // Set message. + $msg = EMOJI_WARN . ' ' . getTranslation('delete_this_raid') . ' ' . EMOJI_WARN . CR . CR; + $msg .= show_raid_poll_small($raid); +} else if ($action == 1) { + debug_log('Raid deletion for ' . $raidId . ' was canceled!'); + // Set message. + $msg = '' . getTranslation('raid_deletion_was_canceled') . ''; + + // Set keys. + $keys = []; +} else if ($action == 2) { + debug_log('Confirmation to delete raid ' . $raidId . ' was received!'); + // Delete telegram messages for raid. + $rs = my_query(' + SELECT * + FROM cleanup + WHERE raid_id = ? + AND chat_id <> 0 + ', [$raidId] + ); + + // Counter + $counter = 0; + + // Delete every telegram message + while ($row = $rs->fetch()) { + // Delete telegram message. + debug_log('Deleting telegram message ' . $row['message_id'] . ' from chat ' . $row['chat_id'] . ' for raid ' . $row['raid_id']); + delete_message($row['chat_id'], $row['message_id']); + $counter = $counter + 1; + } + + // Nothing to delete on telegram. + if ($counter == 0) { + debug_log('Raid with ID ' . $raidId . ' was not found in the cleanup table! Skipping deletion of telegram messages!'); + } + + // Delete raid from cleanup table. + debug_log('Deleting raid ' . $raidId . ' from the cleanup table:'); + my_query(' + DELETE FROM cleanup + WHERE raid_id = ? + ', [$raidId] + ); + + // Delete raid from attendance table. + debug_log('Deleting raid ' . $raidId . ' from the attendance table:'); + my_query(' + DELETE FROM attendance + WHERE raid_id = ? + ', [$raidId] + ); + + // Delete raid from raid table. + debug_log('Deleting raid ' . $raidId . ' from the raid table:'); + my_query(' + DELETE FROM raids + WHERE id = ? + ', [$raidId] + ); + + // Set message. + $msg = getTranslation('raid_successfully_deleted'); + + // Set keys. + $keys = []; } - + // Build callback message string. $callback_response = 'OK'; @@ -77,6 +114,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/raids_list.php b/mods/raids_list.php index a247e7a1..b14c9e97 100644 --- a/mods/raids_list.php +++ b/mods/raids_list.php @@ -1,54 +1,42 @@ accessCheck('list'); // Get raid details. -$raid = get_raid($id); +$raid = get_raid($raidId); // Create keys array. -$keys = [ - [ - [ - 'text' => getTranslation('expand'), - 'callback_data' => $raid['id'] . ':vote_refresh:0', - ] - ], - [ - [ - 'text' => getTranslation('update_pokemon'), - 'callback_data' => $raid['id'] . ':raid_edit_poke:' . $raid['level'], - ] - ], - [ - [ - 'text' => getTranslation('delete'), - 'callback_data' => $raid['id'] . ':raids_delete:0' - ] - ] -]; +$keys = []; +// Probably unused feature. Will fix if someone needs this +// $keys[][] = button(getTranslation('expand'), ['vote_refresh', 'r' => $raid['id']]); +if($botUser->raidaccessCheck($raidId, 'pokemon', true)) { + $keys[][] = button(getTranslation('update_pokemon'), ['raid_edit_poke', 'r' => $raid['id'], 'rl' => $raid['level']]); +} +if($botUser->raidaccessCheck($raidId, 'delete', true)) { + $keys[][] = button(getTranslation('delete'), ['raids_delete', 'r' => $raid['id']]); +} // Add keys to share. debug_log($raid, 'raw raid data for share: '); -$keys_share = share_keys($raid['id'], 'raid_share', $update, '', '', false, $raid['level']); -if(is_array($keys_share)) { - $keys = array_merge($keys, $keys_share); +$keys_share = share_keys($raid['id'], 'raid_share', $update, $raid['level']); +if(!empty($keys_share)) { + $keys = array_merge($keys, $keys_share); } else { - debug_log('There are no groups to share to, is SHARE_CHATS set?'); + debug_log('There are no groups to share to, is SHARE_CHATS set?'); } // Exit key -$empty_exit_key = []; -$key_exit = universal_key($empty_exit_key, '0', 'exit', '1', getTranslation('done')); -$keys = array_merge($keys, $key_exit); +$keys[][] = button(getTranslation('done'), 'exit'); // Get message. $msg = show_raid_poll_small($raid); @@ -67,6 +55,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/refresh_polls.php b/mods/refresh_polls.php index d3d58f73..acac4e23 100644 --- a/mods/refresh_polls.php +++ b/mods/refresh_polls.php @@ -1,39 +1,37 @@ AUTO_REFRESH_POLLS) { - if(strlen($data['id']) > 5) $where_chat = 'chat_id = '.$data['id']; else $where_chat = 'chat_id != 0'; - if(!empty($config->RAID_POLL_HIDE_BUTTONS_RAID_LEVEL)) $level_exclude = 'AND raids.level NOT IN ('.$config->RAID_POLL_HIDE_BUTTONS_RAID_LEVEL.')'; else $level_exclude = ''; - $query_messages = my_query(" - SELECT cleanup.* - FROM cleanup - LEFT JOIN raids - ON cleanup.raid_id = raids.id - WHERE {$where_chat} - AND cleanup.type IN ('poll_text', 'poll_photo') - AND raids.start_time <= UTC_TIMESTAMP() - AND raids.end_time > DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 MINUTE) - AND message_id != 0 - {$level_exclude} - "); - debug_log("REFRESH POLLS:"); - debug_log("Num rows: ".$query_messages->rowCount()); - $tg_json = []; - while($res_messages = $query_messages->fetch()) { +if(!$config->AUTO_REFRESH_POLLS) { + info_log("Automatic refresh of raid polls failed, AUTO_REFRESH_POLLS is set to false in config."); + exit(); +} +if(strlen($data['id']) > 5) $where_chat = 'chat_id = '.$data['id']; else $where_chat = 'chat_id != 0'; +if(!empty($config->RAID_POLL_HIDE_BUTTONS_RAID_LEVEL)) $level_exclude = 'AND raids.level NOT IN ('.$config->RAID_POLL_HIDE_BUTTONS_RAID_LEVEL.')'; else $level_exclude = ''; +$query_messages = my_query(' + SELECT cleanup.raid_id, cleanup.chat_id, cleanup.thread_id, cleanup.message_id, cleanup.type, cleanup.media_unique_id + FROM cleanup + LEFT JOIN raids + ON cleanup.raid_id = raids.id + WHERE ' .$where_chat . ' + AND cleanup.type IN (\'poll_text\', \'poll_photo\', \'photo\') + AND raids.start_time <= UTC_TIMESTAMP() + AND raids.end_time > DATE_SUB(UTC_TIMESTAMP(), INTERVAL 1 MINUTE) + AND message_id != 0 + ' . $level_exclude +); +debug_log("REFRESH POLLS:"); +debug_log("Num rows: ".$query_messages->rowCount()); +$tg_json = []; +while($res_messages = $query_messages->fetch()) { - debug_log("message id: ".$res_messages['message_id']); - debug_log("chat id: ".$res_messages['chat_id']); - debug_log("raid id: ".$res_messages['raid_id']); + debug_log("message id: ".$res_messages['message_id']); + debug_log("chat id: ".$res_messages['chat_id']); + debug_log("raid id: ".$res_messages['raid_id']); - $data_poll['push']['message_id']=$res_messages['message_id']; - $data_poll['push']['chat_id']=$res_messages['chat_id']; - $data_poll['push']['type']=$res_messages['type']; + $data_poll['push'] = $res_messages; - require_once(LOGIC_PATH . '/update_raid_poll.php'); - $tg_json = update_raid_poll($res_messages['raid_id'], false, $data_poll, $tg_json, false); - } - curl_json_multi_request($tg_json); -}else { - info_log("Automatic refresh of raid polls failed, AUTO_REFRESH_POLLS is set to false in config."); + require_once(LOGIC_PATH . '/update_raid_poll.php'); + $tg_json = update_raid_poll($res_messages['raid_id'], get_raid($res_messages['raid_id']), $data_poll, $tg_json); } -exit(); \ No newline at end of file +curl_json_multi_request($tg_json); +exit(); diff --git a/mods/save_event_note.php b/mods/save_event_note.php index f00cb97f..43be6af5 100644 --- a/mods/save_event_note.php +++ b/mods/save_event_note.php @@ -1,6 +1,7 @@ prepare("UPDATE raids SET event_note=:text WHERE id = :id"); -$query->execute([':text' => $update['message']['text'], ':id' => $raid_id]); +my_query('UPDATE raids SET event_note=:text WHERE id = :id', [':text' => $update['message']['text'], ':id' => $raid_id]); // Remove back button from previous message to avoid confusion edit_message_keyboard($modifiers['old_message_id'], [], $user_id); // Return message to user +$raid = get_raid($raid_id); $msg = ''; $msg .= getTranslation('raid_saved') . CR; $msg .= CR.getTranslation('event_note').': '.$update['message']['text'].CR2; -$msg .= show_raid_poll_small(get_raid($raid_id)) . CR; +$msg .= show_raid_poll_small($raid) . CR; debug_log($msg); -$keys = [ - [ - [ - 'text' => getTranslation('event_note_edit'), - 'callback_data' => $raid_id . ':edit_event_note:edit' - ] - ], - [ - [ - 'text' => getTranslation('delete'), - 'callback_data' => $raid_id . ':raids_delete:0' - ] - ] -]; -$keys_share = share_keys($raid_id, 'raid_share', $update, $chats); +$keys[][] = button(getTranslation('event_note_edit'), ['edit_event_note', 'r' => $raid_id, 'm' => 'e']); +$keys[][] = button(getTranslation('delete'), ['raids_delete', 'r' => $raid_id]); +$keys_share = share_keys($raid_id, 'raid_share', $update, $raid['level']); $keys = array_merge($keys, $keys_share); debug_log($keys); // Send response message to user -send_message($user_id, $msg, $keys, []); +send_message(create_chat_object([$user_id]), $msg, $keys, []); diff --git a/mods/save_gym_info.php b/mods/save_gym_info.php new file mode 100644 index 00000000..2d8140da --- /dev/null +++ b/mods/save_gym_info.php @@ -0,0 +1,55 @@ + $lat, + ':lon' => $lon, + ':id' => $gym_id, + ]; + $gym['lat'] = $lat; + $gym['lon'] = $lon; +}else if(in_array($action, ['addr','name','note'])) { + if(strlen($input) > 255) { + send_message(create_chat_object([$user_id]), getTranslation('gym_edit_text_too_long')); + exit(); + } + $column_map = ['addr' => 'address', 'name' => 'gym_name', 'note' => 'gym_note']; + $query = 'UPDATE gyms SET ' . $column_map[$action] . ' = :value WHERE id = :id'; + $binds = [ + ':value' => $input, + ':id' => $gym_id, + ]; + $gym[$column_map[$action]] = $input; +} +if($query !== false) { + my_query($query, $binds); + + $msg = get_gym_details($gym, true); + $msg .= CR . CR . getTranslation('gym_saved'); + $update['callback_query']['from']['id'] = $user_id; + $keys = edit_gym_keys($update, $gym_id, $gym['show_gym'], $gym['ex_gym'], $gym['gym_note'], $gym['address']); +} +// Remove back button from previous message to avoid confusion +editMessageText($modifiers['old_message_id'], $msg, $keys, $user_id, ['disable_web_page_preview' => 'true']); diff --git a/mods/share_raid_by_location.php b/mods/share_raid_by_location.php index 2f542ee0..2435d262 100644 --- a/mods/share_raid_by_location.php +++ b/mods/share_raid_by_location.php @@ -1,155 +1,140 @@ ' . getTranslation('invalid_input') . ''); - exit(); - } - $gps_diff = (float)0.01; - - // Build query. - $rs = my_query( - ' - SELECT raids.*, - gyms.lat, gyms.lon, gyms.address, gyms.gym_name, gyms.ex_gym, - users.name, - TIME_FORMAT(TIMEDIFF(raids.end_time, UTC_TIMESTAMP()) + INTERVAL 1 MINUTE, \'%k:%i\') AS t_left - FROM raids - LEFT JOIN gyms - ON raids.gym_id = gyms.id - LEFT JOIN users - ON raids.user_id = users.user_id - WHERE raids.end_time>UTC_TIMESTAMP() - AND gyms.lat BETWEEN \''.($lat-$gps_diff).'\' AND \''.($lat+$gps_diff).'\' - AND gyms.lon BETWEEN \''.($lon-$gps_diff).'\' AND \''.($lon+$gps_diff).'\' - ORDER BY raids.end_time ASC LIMIT 20 - ' - ); - - // Count results. - $count = 0; - - // Init text and keys. - $text = ''; - $keys = []; - - // Get raids. - while ($raid = $rs->fetch()) { - // Set text and keys. - $gym_name = $raid['gym_name']; - if(empty($gym_name)) { - $gym_name = ''; - } - - $text .= $gym_name . CR; - $raid_day = dt2date($raid['start_time']); - $now = utcnow(); - $today = dt2date($now); - $start = dt2time($raid['start_time']); - $end = dt2time($raid['end_time']); - $time_left = $raid['t_left']; - if ($now < $start) { - $text .= get_raid_times($raid, true); - // Raid has started already - } else { - // Add time left message. - $text .= get_local_pokemon_name($raid['pokemon'], $raid['pokemon_form']) . ' — ' . getPublicTranslation('still') . SP . $time_left . 'h' . CR . CR; - } - - - // Split pokemon and form to get the pokedex id. - $pokedex_id = explode('-', $raid['pokemon'])[0]; - - // Pokemon is an egg? - $eggs = $GLOBALS['eggs']; - if(in_array($pokedex_id, $eggs)) { - $keys_text = EMOJI_EGG . SP . $gym_name; - } else { - $keys_text = $gym_name; - } - - $keys[] = array( - 'text' => $keys_text, - 'callback_data' => $raid['id'] . ':share_raid_by_location:1' - ); - - // Counter++ - $count = $count + 1; - } - - // Set message. - if($count == 0) { - $msg = '' . getTranslation('no_active_raids_found') . ''; - } else { - // Get the inline key array. - $keys = inline_key_array($keys, 1); - - // Add exit key. - $keys[] = [ - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ]; - - // Build message. - $msg = '' . getTranslation('list_all_active_raids') . ':' . CR; - $msg .= $text; - $msg .= '' . getTranslation('select_gym_name') . '' . CR; - } - - // Send message. - send_message($update['message']['chat']['id'], $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); +$botUser->accessCheck('share-all'); + +if(isset($data['a']) && $data['a'] == 1) { + $raid_id = $data['r']; + + // Get raid details. + $raid = get_raid($raid_id); + + $keys = []; + + // Add keys to share. + debug_log($raid, 'raw raid data for share: '); + $keys_share = share_keys($raid['id'], 'raid_share', $update, $raid['level']); + if(!empty($keys_share)) { + $keys = array_merge($keys, $keys_share); + } else { + debug_log('There are no groups to share to, is SHARE_CHATS set?'); + } + // Exit key + $keys[][] = button(getTranslation('done'), ['exit', 'd' => 1]); + + // Get message. + $msg = show_raid_poll_small($raid); + + // Build callback message string. + $callback_response = 'OK'; + + // Telegram JSON array. + $tg_json = array(); + + // Answer callback. + $tg_json[] = answerCallbackQuery($update['callback_query']['id'], $callback_response, true); + + // Edit message. + $tg_json[] = edit_message($update, $msg, $keys, false, true); + + // Telegram multicurl request. + curl_json_multi_request($tg_json); + exit; +} +if(!isset($update['message']['location'])) { + send_message(create_chat_object([$update['message']['chat']['id']]), '' . getTranslation('invalid_input') . ''); + exit(); +} +$lat = (float)$update['message']['location']['latitude']; +$lon = (float)$update['message']['location']['longitude']; +$gps_diff = (float)0.01; + +// Build query. +$rs = my_query(' + SELECT raids.*, + gyms.lat, gyms.lon, gyms.address, gyms.gym_name, gyms.ex_gym, + users.name, + TIME_FORMAT(TIMEDIFF(raids.end_time, UTC_TIMESTAMP()) + INTERVAL 1 MINUTE, \'%k:%i\') AS t_left + FROM raids + LEFT JOIN gyms + ON raids.gym_id = gyms.id + LEFT JOIN users + ON raids.user_id = users.user_id + WHERE raids.end_time>UTC_TIMESTAMP() + AND gyms.lat BETWEEN \''.($lat-$gps_diff).'\' AND \''.($lat+$gps_diff).'\' + AND gyms.lon BETWEEN \''.($lon-$gps_diff).'\' AND \''.($lon+$gps_diff).'\' + ORDER BY raids.end_time ASC LIMIT 20 +'); + +// Count results. +$count = 0; + +// Init text and keys. +$text = ''; +$keys = []; + +// Get raids. +while ($raid = $rs->fetch()) { + // Set text and keys. + $gym_name = $raid['gym_name']; + if(empty($gym_name)) { + $gym_name = ''; + } + + $text .= $gym_name . CR; + $raid_day = dt2date($raid['start_time']); + $now = utcnow(); + $today = dt2date($now); + $start = dt2time($raid['start_time']); + $end = dt2time($raid['end_time']); + $time_left = $raid['t_left']; + if ($now < $start) { + $text .= get_raid_times($raid, $botUser->userLanguage); + // Raid has started already + } else { + // Add time left message. + $text .= get_local_pokemon_name($raid['pokemon'], $raid['pokemon_form']) . ' — ' . getPublicTranslation('still') . SP . $time_left . 'h' . CR . CR; + } + + // Split pokemon and form to get the pokedex id. + $pokedex_id = explode('-', $raid['pokemon'])[0]; + + // Pokemon is an egg? + if(in_array($pokedex_id, EGGS)) { + $keys_text = EMOJI_EGG . SP . $gym_name; + } else { + $keys_text = $gym_name; + } + + $keys[] = button($keys_text, ['share_raid_by_location', 'r' => $raid['id'], 'a' => 1]); + + // Counter++ + $count = $count + 1; +} + +// Set message. +if($count == 0) { + $msg = '' . getTranslation('no_active_raids_found') . ''; +} else { + // Get the inline key array. + $keys = inline_key_array($keys, 1); + + // Add exit key. + $keys[][] = button(getTranslation('abort'), 'exit'); + + // Build message. + $msg = '' . getTranslation('list_all_active_raids') . ':' . CR; + $msg .= $text; + $msg .= '' . getTranslation('select_gym_name') . '' . CR; } -?> + +// Send message. +send_message(create_chat_object([$update['message']['chat']['id']]), $msg, $keys, ['reply_markup' => ['selective' => true, 'one_time_keyboard' => true]]); diff --git a/mods/trainer.php b/mods/trainer.php index fb3187b9..2db8f149 100644 --- a/mods/trainer.php +++ b/mods/trainer.php @@ -1,102 +1,72 @@ accessCheck('trainer'); $user_id = $update['callback_query']['from']['id']; -if($data['arg'] == "a") { - my_query("UPDATE users SET auto_alarm = IF(auto_alarm = 1, 0, 1) WHERE user_id = {$user_id}"); +if(isset($data['a']) && $data['a'] == 1) { + my_query('UPDATE users SET auto_alarm = IF(auto_alarm = 1, 0, 1) WHERE user_id = ?', [$user_id]); } // Set message. $msg = '' . getTranslation('trainerinfo_set_yours') . ''; -$msg .= CR.CR.get_user($user_id, false); +$msg .= CR . CR . get_user($user_id, false); // Init empty keys array. $keys = []; // Create keys array. -if($config->CUSTOM_TRAINERNAME){ - $keys[0][] = - [ - 'text' => getTranslation('name'), - 'callback_data' => '0:trainer_name:0' - ]; +if($config->CUSTOM_TRAINERNAME) { + $keys[0][] = button(getTranslation('name'), 'trainer_name'); } -if($config->RAID_POLL_SHOW_TRAINERCODE){ - $keys[0][] = - [ - 'text' => getTranslation('trainercode'), - 'callback_data' => '0:trainer_code:0' - ]; +if($config->RAID_POLL_SHOW_TRAINERCODE) { + $keys[0][] = button(getTranslation('trainercode'), 'trainer_code'); } $keys[] = [ - [ - 'text' => getTranslation('team'), - 'callback_data' => '0:trainer_team:0' - ], - [ - 'text' => getTranslation('level'), - 'callback_data' => '0:trainer_level:0' - ] + button(getTranslation('team'), 'trainer_team'), + button(getTranslation('level'), 'trainer_level') ]; -if ($config->RAID_AUTOMATIC_ALARM == false) { - $q_user = my_query("SELECT auto_alarm FROM users WHERE user_id = '{$user_id}' LIMIT 1"); - $alarm_status = $q_user->fetch()['auto_alarm']; - $keys[] = [ - [ - 'text' => ($alarm_status == 1 ? getTranslation('switch_alarm_off') . ' ' . EMOJI_NO_ALARM : getTranslation('switch_alarm_on') . ' ' . EMOJI_ALARM), - 'callback_data' => '0:trainer:a' - ] - ]; +if($config->RAID_AUTOMATIC_ALARM == false) { + $q_user = my_query('SELECT auto_alarm FROM users WHERE user_id = ? LIMIT 1', [$user_id]); + $alarm_status = $q_user->fetch()['auto_alarm']; + $keys[][] = button( + ($alarm_status == 1 ? getTranslation('switch_alarm_off') . ' ' . EMOJI_NO_ALARM : getTranslation('switch_alarm_on') . ' ' . EMOJI_ALARM), + ['trainer', 'a' => 1] + ); } -if ($config->LANGUAGE_PRIVATE == '') { - $keys[] = [ - [ - 'text' => getTranslation('bot_lang'), - 'callback_data' => '0:bot_lang:0' - ] - ]; +if($config->LANGUAGE_PRIVATE == '') { + $keys[][] = button(getTranslation('bot_lang'), 'bot_lang'); +} +if ($config->ENABLE_GYM_AREAS == true) { + $keys[][] = button(getTranslation('default_gymarea'), 'trainerGymarea'); } - -// Check access. -$access = bot_access_check($update, 'trainer-share', true, true); // Display sharing options for admins and users with trainer-share permissions -if($access && (is_file(ROOT_PATH . '/access/' . $access) || $access == 'BOT_ADMINS')) { - // Add sharing keys. - $share_keys = []; - $share_keys[] = universal_inner_key($keys, '0', 'trainer_add', '0', getTranslation('trainer_message_share')); - $share_keys[] = universal_inner_key($keys, '0', 'trainer_delete', '0', getTranslation('trainer_message_delete')); +if($botUser->accessCheck('trainer-share', true)) { + // Add sharing keys. + $share_keys = []; + $share_keys[] = button(getTranslation('trainer_message_share'), 'trainer_add'); + $share_keys[] = button(getTranslation('trainer_message_delete'), 'trainer_delete'); - // Get the inline key array. - $keys[] = $share_keys; + // Get the inline key array. + $keys[] = $share_keys; - // Add message. - $msg .= CR . CR . getTranslation('trainer_message_share_or_delete'); + // Add message. + $msg .= CR . CR . getTranslation('trainer_message_share_or_delete'); } -// Add abort key. -$nav_keys = []; -$nav_keys[] = universal_inner_key($keys, '0', 'exit', '0', getTranslation('abort')); - // Get the inline key array. -$keys[] = $nav_keys; +$keys[][] = button(getTranslation('done'), ['exit', 'd' => 1]); // Answer callback. answerCallbackQuery($update['callback_query']['id'], 'OK'); // Edit message. edit_message($update, $msg, $keys, false); - -// Exit. -$dbh = null; -exit(); - -?> \ No newline at end of file diff --git a/mods/trainerGymarea.php b/mods/trainerGymarea.php new file mode 100644 index 00000000..f011c5d3 --- /dev/null +++ b/mods/trainerGymarea.php @@ -0,0 +1,48 @@ +accessCheck('trainer'); + +$gymarea = $data['i'] ?? false; + +if($gymarea !== false) { + my_query('UPDATE users SET gymarea = ? WHERE user_id = ?',[$gymarea, $botUser->userId]); +}else { + $q = my_query('SELECT gymarea FROM users WHERE user_id = ? LIMIT 1', [$botUser->userId]); + $gymarea = $q->fetch()['gymarea']; +} + +// Init empty keys array. +$keys = []; + +$json = json_decode(file_get_contents(botSpecificConfigFile('geoconfig_gym_areas.json')), 1); +$gymareaName = ''; +foreach($json as $area) { + if($area['id'] == $gymarea) $gymareaName = $area['name']; + $keys[] = button($area['name'], ['trainerGymarea', 'i' => $area['id']]); + +} +$keys = inline_key_array($keys, 2); +$keys[] = [ + button(getTranslation('back'), ['trainer', 'arg' => 0]), + button(getTranslation('done'), ['exit', 'd' => 1]) +]; +// Set message. +$msg = '' . getTranslation('trainerinfo_set_yours') . ''; + +$msg .= CR . CR . get_user($botUser->userId, true); +$msg .= '' . getTranslation('default_gymarea') . ': '; +$msg .= $gymareaName; + +// Answer callback. +answerCallbackQuery($update['callback_query']['id'], 'OK'); + +// Edit message. +edit_message($update, $msg, $keys, false); diff --git a/mods/trainer_add.php b/mods/trainer_add.php index baf1b208..d83ac3f0 100644 --- a/mods/trainer_add.php +++ b/mods/trainer_add.php @@ -1,104 +1,71 @@ accessCheck('trainer-share'); // Init keys and chat list. $keys = []; -$chat_list = ''; -// $config->TRAINER_CHATS ? -if(!empty($config->TRAINER_CHATS)) { - $chat_list = $config->TRAINER_CHATS; - debug_log($chat_list, 'Added trainer chats to the chat list:'); -} - -// $config->SHARE_CHATS ? -if(!empty($config->SHARE_CHATS) && !empty($chat_list)) { - $chat_list .= ',' . $config->SHARE_CHATS; - debug_log($chat_list, 'Added share chats to the chat list:'); -} else if(!empty($config->SHARE_CHATS) && empty($chat_list)) { - $chat_list = $config->SHARE_CHATS; - debug_log($chat_list, 'Added share chats to the chat list:'); -} - -// Get chats from config and add to keys. -for($i = 1; $i <= 6; $i++) { - // Raid level adjustment - if($i == 6) { - $raid_level = 'X'; - } else { - $raid_level = $i; - } - $const = 'SHARE_CHATS_LEVEL_' . $raid_level; - $const_chats = $config->{$const}; - - // Sharing keys for this raid level? - if(!empty($const_chats)) { - debug_log('Found chats by level, adding them'); - // Add chats. - if(!empty($chat_list)) { - $chat_list .= ',' . $const_chats; - debug_log($chat_list, 'Added ' . $const . ' chats to the chat list:'); - } else { - $chat_list = $const_chats; - debug_log($chat_list, 'Added ' . $const . ' chats to the chat list:'); - } - } -} - -// Delete duplicate chats. -debug_log($chat_list, 'Searching and removing duplicates from chat list:'); -$chat_list = explode(',', $chat_list); -$chats = array_unique($chat_list); +$chats = list_config_chats_by_short_id(); // Get chats already in the database. debug_log('Searching and removing chats already having the trainer message'); -$rs = my_query( - " - SELECT chat_id - FROM trainerinfo - " -); - -$chats_db = []; -while ($row = $rs->fetch()) { - $chats_db[] = $row['chat_id']; +$rs = my_query(' + SELECT chat_id, thread_id + FROM trainerinfo +'); + +$chats_db = $rs->fetchAll(); +for($i=0;$i $chat) { + // Get chat object + debug_log("Getting chat object for '" . $chat['id'] . "'"); + $chat_obj = get_chat($chat['id']); + + // Check chat object for proper response. + if ($chat_obj['ok'] != true) { + info_log($chat, 'Invalid chat id in your configuration:'); + continue; + } + $chatTitle = $chat['title'] ?? $chat_obj['result']['title']; + debug_log('Proper chat object received, continuing to add key for this chat: ' . $chatTitle); + $shareData = [0 => 'trainer_share', 'c' => $chatShortId]; + $keys[][] = button(getTranslation('share_with') . ' ' . $chatTitle, $shareData); } // Add abort key. if($keys) { - // Add back navigation key. - $nav_keys = []; - $nav_keys[] = universal_inner_key($keys, '0', 'trainer', '0', getTranslation('back')); - $nav_keys[] = universal_inner_key($keys, '0', 'exit', '0', getTranslation('abort')); + // Add back navigation key. + $nav_keys = []; + $nav_keys[] = button(getTranslation('back'), 'trainer'); + $nav_keys[] = button(getTranslation('abort'), 'exit'); - // Get the inline key array. - $keys[] = $nav_keys; + // Get the inline key array. + $keys[] = $nav_keys; - // Set message. - $msg = '' . getTranslation('trainer_info_share_with_chat') . ''; + // Set message. + $msg = '' . getTranslation('trainer_info_share_with_chat') . ''; } else { - // Set message. - $msg = '' . getTranslation('trainer_info_no_chats') . ''; + // Set message. + $msg = '' . getTranslation('trainer_info_no_chats') . ''; } // Answer callback. @@ -106,5 +73,3 @@ // Edit message. edit_message($update, $msg, $keys, false); - -?> diff --git a/mods/trainer_code.php b/mods/trainer_code.php index 0ced1b15..2d3a7acf 100644 --- a/mods/trainer_code.php +++ b/mods/trainer_code.php @@ -1,76 +1,67 @@ accessCheck('trainer'); // Mode and action -$mode = $data['id']; -$action = $data['arg']; +$action = $data['a'] ?? ''; // Set the user_id $user_id = $update['callback_query']['from']['id']; -if($action == "cancel") { - my_query("DELETE FROM user_input WHERE user_id='{$user_id}' AND handler='change_trainercode'"); - - // Build callback message string. - $callback_response = 'OK'; - - $data['arg'] = $data['id'] = 0; - require_once(ROOT_PATH . '/mods/trainer.php'); -}elseif($action == "delete") { - my_query("DELETE FROM user_input WHERE user_id='{$user_id}' AND handler='change_trainercode'"); - my_query(" - UPDATE users - SET trainercode = NULL - WHERE user_id = {$user_id} - "); - - // Build callback message string. - $callback_response = 'OK'; - - $data['arg'] = $data['id'] = 0; - require_once(ROOT_PATH . '/mods/trainer.php'); -}else { - $user_data = get_user($user_id, false, true); - // Build message string. - $msg = '' . getTranslation('your_trainer_info') . '' . CR; - $msg .= $user_data['message'] . CR; - - // Save the message id to db so we can delete it later - $modifiers = json_encode(["old_message_id"=>$update['callback_query']['message']['message_id']]); - - $msg .= '' . getTranslation('trainercode_select') . ''; - // Data for handling response from the user - my_query("INSERT INTO user_input SET user_id='{$user_id}', handler='change_trainercode', modifiers='{$modifiers}' "); - - // Build callback message string. - $callback_response = 'OK'; - - $keys[] = [ - [ - 'text' => getTranslation('back'), - 'callback_data' => '0:trainer_code:cancel' - ],[ - 'text' => getTranslation('delete'), - 'callback_data' => '0:trainer_code:delete' - ] - ]; - // Answer callback. - answerCallbackQuery($update['callback_query']['id'], $callback_response); - - // Edit message. - edit_message($update, $msg, $keys, false); +if($action == 'cancel') { + my_query('DELETE FROM user_input WHERE user_id = ? AND handler = \'change_trainercode\'', [$user_id]); + + // Build callback message string. + $callback_response = 'OK'; + + $data['arg'] = $data['id'] = 0; + require_once(ROOT_PATH . '/mods/trainer.php'); + exit; +}elseif($action == 'delete') { + my_query('DELETE FROM user_input WHERE user_id = :user_id AND handler=\'change_trainercode\'', ['user_id' => $user_id]); + my_query(' + UPDATE users + SET trainercode = NULL + WHERE user_id = ? + ',[$user_id + ]); + + // Build callback message string. + $callback_response = 'OK'; + + $data['arg'] = $data['id'] = 0; + require_once(ROOT_PATH . '/mods/trainer.php'); + exit; } - -// Exit. -$dbh = null; -exit(); - -?> +$user_data = get_user($user_id, false, true); +// Build message string. +$msg = '' . getTranslation('your_trainer_info') . '' . CR; +$msg .= $user_data['message'] . CR; + +// Save the message id to db so we can delete it later +$modifiers = json_encode(['old_message_id'=>$update['callback_query']['message']['message_id']]); + +$msg .= '' . getTranslation('trainercode_select') . ''; +// Data for handling response from the user +my_query('INSERT INTO user_input SET user_id = ?, handler = \'change_trainercode\', modifiers = ?',[$user_id, $modifiers]); + +// Build callback message string. +$callback_response = 'OK'; + +$keys[] = [ + button(getTranslation('back'), ['trainer_code', 'a' => 'cancel']), + button(getTranslation('delete'), ['trainer_code', 'a' => 'delete']) +]; +// Answer callback. +answerCallbackQuery($update['callback_query']['id'], $callback_response); + +// Edit message. +edit_message($update, $msg, $keys, false); diff --git a/mods/trainer_delete.php b/mods/trainer_delete.php index f02deadf..383f8b7c 100644 --- a/mods/trainer_delete.php +++ b/mods/trainer_delete.php @@ -1,140 +1,92 @@ accessCheck('trainer-delete'); // Init keys and chat list. $keys = []; // Get chat id and action -$trainer_chat = $data['id']; -$action = $data['arg']; +$trainer_chat = $data['c'] ?? 0; +$action = $data['a'] ?? 0; // Show chats to delete if($action == 0 || $trainer_chat == 0) { - debug_log('Getting chats the trainer message was shared with'); - $rs = my_query( - " - SELECT * - FROM trainerinfo - " - ); - - while ($row = $rs->fetch()) { - // Chat and message ID - $chat_id = $row['chat_id']; - $message_id = $row['message_id']; - - // Get info about chat for title. - debug_log('Getting chat object for chat_id: ' . $chat_id); - $chat_obj = get_chat($chat_id); - $chat_title = ''; - - // Set title. - if ($chat_obj['ok'] == 'true') { - $chat_title = $chat_obj['result']['title']; - debug_log('Title of the chat: ' . $chat_obj['result']['title']); - } else { - $chat_title = $chat_id; - } - - $keys[] = universal_inner_key($keys, $chat_id, 'trainer_delete', '1', $chat_title); - } - - // Add abort key. - if($keys) { - // Inline key array. - $keys = inline_key_array($keys, 1); - - // Add back navigation key. - $nav_keys = []; - $nav_keys[] = universal_inner_key($keys, '0', 'trainer', '0', getTranslation('back')); - $nav_keys[] = universal_inner_key($keys, '0', 'exit', '0', getTranslation('abort')); - - // Get the inline key array. - $keys[] = $nav_keys; - - // Set message. - $msg = '' . getTranslation('trainer_message_delete') . '?'; - } else { - // Set message. - $msg = '' . getTranslation('trainer_info_no_chats') . ''; - } + debug_log('Getting chats the trainer message was shared with'); + $rs = my_query(' + SELECT chat_id + FROM trainerinfo + '); + + while ($row = $rs->fetch()) { + // Chat and message ID + $chat_id = $row['chat_id']; + [$chat_title, $chat_username] = get_chat_title_username($chat_id); + + $keys[] = button($chat_title, ['trainer_delete', 'c' => $chat_id, 'a' => 1]); + } + + // Add abort key. + if($keys) { + // Inline key array. + $keys = inline_key_array($keys, 1); + + // Add back navigation key. + $nav_keys = []; + $nav_keys[] = button(getTranslation('back'), 'trainer'); + $nav_keys[] = button(getTranslation('abort'), 'exit'); + + // Get the inline key array. + $keys[] = $nav_keys; + + // Set message. + $msg = '' . getTranslation('trainer_message_delete') . '?'; + } else { + // Set message. + $msg = '' . getTranslation('trainer_info_no_chats') . ''; + } // Confirm deletion } else if($action == 1 && $trainer_chat != 0) { - // Get info about chat for title. - debug_log('Getting chat object for chat_id: ' . $trainer_chat); - $chat_obj = get_chat($trainer_chat); - $chat_title = ''; - - // Set title. - if ($chat_obj['ok'] == 'true') { - $chat_title = $chat_obj['result']['title']; - debug_log('Title of the chat: ' . $chat_obj['result']['title']); - } else { - $chat_title = $trainer_chat; - } - - // Set message - $msg = $chat_title . CR . CR; - $msg .= EMOJI_WARN . SP . '' . getTranslation('delete_trainer_message_from_chat') . '' . SP . EMOJI_WARN; - - // Create the keys. - $keys = [ - [ - [ - 'text' => getTranslation('yes'), - 'callback_data' => $trainer_chat . ':trainer_delete:2' - ] - ], - [ - [ - 'text' => getTranslation('no'), - 'callback_data' => '0:exit:0' - ] - ] - ]; + [$chat_title, $chat_username] = get_chat_title_username($trainer_chat); + + // Set message + $msg = $chat_title . CR . CR; + $msg .= EMOJI_WARN . SP . '' . getTranslation('delete_trainer_message_from_chat') . '' . SP . EMOJI_WARN; + + // Create the keys. + $keys[][] = button(getTranslation('yes'), ['trainer_delete', 'c' => $trainer_chat, 'a' => 2]); + $keys[][] = button(getTranslation('no'), 'exit'); // Delete trainer message } else if($action == 2 && $trainer_chat != 0) { - debug_log('Deleting trainer message from chat ' . $trainer_chat); - // Get info about chat for title. - debug_log('Getting chat object for chat_id: ' . $trainer_chat); - $chat_obj = get_chat($trainer_chat); - $chat_title = ''; - - // Set title. - if ($chat_obj['ok'] == 'true') { - $chat_title = $chat_obj['result']['title']; - debug_log('Title of the chat: ' . $chat_obj['result']['title']); - } else { - $chat_title = $trainer_chat; - } - - // Set message - $msg = '' . getTranslation('deleted_trainer_message') . '' . CR; - - // Get trainer messages - debug_log('Getting chats the trainer message was shared with'); - $rs = my_query( - " - SELECT * - FROM trainerinfo - WHERE chat_id = '{$trainer_chat}' - " - ); - - // Delete trainer message. - while ($row = $rs->fetch()) { - delete_trainerinfo($row['chat_id'], $row['message_id']); - } + require_once(LOGIC_PATH . '/delete_trainerinfo.php'); + debug_log('Deleting trainer message from chat ' . $trainer_chat); + [$chat_title, $chat_username] = get_chat_title_username($trainer_chat); + + // Set message + $msg = '' . getTranslation('deleted_trainer_message') . '' . CR; + + // Get trainer messages + debug_log('Getting chats the trainer message was shared with'); + $rs = my_query(' + SELECT message_id + FROM trainerinfo + WHERE chat_id = ? + ', [$trainer_chat] + ); + + // Delete trainer message. + while ($row = $rs->fetch()) { + delete_trainerinfo($trainer_chat, $row['message_id']); + } } // Answer callback. @@ -142,5 +94,3 @@ // Edit message. edit_message($update, $msg, $keys, false); - -?> diff --git a/mods/trainer_level.php b/mods/trainer_level.php index 49ebf21b..6c89f32b 100644 --- a/mods/trainer_level.php +++ b/mods/trainer_level.php @@ -1,85 +1,69 @@ accessCheck('trainer'); -// Confirmation and level -$confirm = $data['id']; -$level = $data['arg']; +$level = $data['l'] ?? 0; // Set the user_id $user_id = $update['callback_query']['from']['id']; // Ask for user level -if($confirm == 0) { - // Build message string. - $msg = '' . getTranslation('your_trainer_info') . '' . CR; - $msg .= get_user($user_id) . CR; - $msg .= '' . getTranslation('level_select') . ''; - - // Set keys. - $keys = []; - for($i = 5; $i <= $config->TRAINER_MAX_LEVEL; $i++) { - $keys[] = array( - 'text' => $i, - 'callback_data' => '1:trainer_level:' . $i - ); - } - - // Get the inline key array. - $keys = inline_key_array($keys, 5); - - // Add navigation keys. - $nav_keys = []; - $nav_keys[] = universal_inner_key($nav_keys, '0', 'trainer', '0', getTranslation('back')); - $nav_keys[] = universal_inner_key($nav_keys, '0', 'exit', '0', getTranslation('abort')); - $nav_keys = inline_key_array($nav_keys, 2); - - // Merge keys. - $keys = array_merge($keys, $nav_keys); - - // Build callback message string. - $callback_response = 'OK'; +if($level == 0) { + // Build message string. + $msg = '' . getTranslation('your_trainer_info') . '' . CR; + $msg .= get_user($user_id) . CR; + $msg .= '' . getTranslation('level_select') . ''; + + // Set keys. + $keys = []; + for($i = 5; $i <= $config->TRAINER_MAX_LEVEL; $i++) { + $keys[] = button($i, ['trainer_level', 'l' => $i]); + } + + // Get the inline key array. + $keys = inline_key_array($keys, 5); + + // Add navigation keys. + $keys[] = [ + button(getTranslation('back'), 'trainer'), + button(getTranslation('done'), ['exit', 'd' => '1']) + ]; + + // Build callback message string. + $callback_response = 'OK'; // Save user level -} else if($confirm == 1 && $level > 0) { - - // Update the user. - my_query( - " - UPDATE users - SET level = {$level} - WHERE user_id = {$user_id} - " - ); - - // Build message string. - $msg = '' . getTranslation('level_saved') . '' . CR . CR; - $msg .= '' . getTranslation('your_trainer_info') . '' . CR; - $msg .= get_user($user_id) . CR; - - // Build callback message string. - $callback_response = 'OK'; - - // Create the keys. - $keys = [ - [ - [ - 'text' => getTranslation('back'), - 'callback_data' => '0:trainer:0' - ], - [ - 'text' => getTranslation('done'), - 'callback_data' => '0:exit:1' - ] - ] - ]; +} else { + + // Update the user. + my_query(' + UPDATE users + SET level = ? + WHERE user_id = ? + ', [$level, $user_id] + ); + + // Build message string. + $msg = '' . getTranslation('level_saved') . '' . CR . CR; + $msg .= '' . getTranslation('your_trainer_info') . '' . CR; + $msg .= get_user($user_id) . CR; + + // Build callback message string. + $callback_response = 'OK'; + + // Create the keys. + $keys[] = [ + button(getTranslation('back'), 'trainer'), + button(getTranslation('done'), ['exit', 'd' => '1']) + ]; } // Answer callback. @@ -87,6 +71,3 @@ // Edit message. edit_message($update, $msg, $keys, false); - -// Exit. -exit(); diff --git a/mods/trainer_name.php b/mods/trainer_name.php index 105a7a99..75b23bac 100644 --- a/mods/trainer_name.php +++ b/mods/trainer_name.php @@ -1,51 +1,53 @@ accessCheck('trainer'); -// Mode and action -$mode = $data['id']; -$action = $data['arg']; +$action = $data['a'] ?? ''; // Set the user_id -$user_id = $update['callback_query']['from']['id']; - -if($action == "cancel") { - my_query("DELETE FROM user_input WHERE user_id='{$user_id}' AND handler='change_trainername'"); - - // Build callback message string. - $callback_response = 'OK'; - - $data['arg'] = $data['id'] = 0; - require_once(ROOT_PATH . '/mods/trainer.php'); -}elseif($action == "delete") { - my_query("DELETE FROM user_input WHERE user_id='{$user_id}' AND handler='change_trainername'"); - my_query(" - UPDATE users - SET trainername = NULL - WHERE user_id = {$user_id} - "); - - // Build callback message string. - $callback_response = 'OK'; - - $data['arg'] = $data['id'] = 0; - require_once(ROOT_PATH . '/mods/trainer.php'); -}elseif($action == "switch") { - my_query(" - UPDATE users - SET display_name = IF(display_name = 0,1,0) - WHERE user_id = {$user_id} - "); - - // Build callback message string. - $callback_response = 'OK'; +$user_id = $botUser->userId; + +if($action == 'cancel') { + my_query('DELETE FROM user_input WHERE user_id = ? AND handler=\'change_trainername\'', [$user_id]); + + // Build callback message string. + $callback_response = 'OK'; + + $data['arg'] = $data['id'] = 0; + require_once(ROOT_PATH . '/mods/trainer.php'); + exit; +}elseif($action == 'delete') { + my_query('DELETE FROM user_input WHERE user_id = ? AND handler = \'change_trainername\'', [$user_id]); + my_query(' + UPDATE users + SET trainername = NULL + WHERE user_id = ? + ', [$user_id] + ); + + // Build callback message string. + $callback_response = 'OK'; + + $data['arg'] = $data['id'] = 0; + require_once(ROOT_PATH . '/mods/trainer.php'); +}elseif($action == 'switch') { + my_query(' + UPDATE users + SET display_name = IF(display_name = 0,1,0) + WHERE user_id = ? + ', [$user_id] + ); + + // Build callback message string. + $callback_response = 'OK'; } $user_data = get_user($user_id, false, true); // Build message string. @@ -56,55 +58,28 @@ $modifiers = json_encode(["old_message_id"=>$update['callback_query']['message']['message_id']]); if($action == 'add') { - $msg .= '' . getTranslation('trainername_select') . ''; - // Data for handling response from the user - my_query("INSERT INTO user_input SET user_id='{$user_id}', handler='change_trainername', modifiers='{$modifiers}' "); + $msg .= '' . getTranslation('trainername_select') . ''; + // Data for handling response from the user + my_query('INSERT INTO user_input SET user_id = ?, handler = \'change_trainername\', modifiers = ?', [$user_id, $modifiers]); } // Build callback message string. $callback_response = 'OK'; if($action != 'add') { - if(!empty($user_data['row']['trainername'])) { - $keys[] = [ - [ - 'text' => getTranslation('switch_display_name'), - 'callback_data' => '0:trainer_name:switch' - ] - ]; - $keys[] = [ - [ - 'text' => getTranslation('trainername_edit'), - 'callback_data' => '0:trainer_name:add' - ],[ - 'text' => getTranslation('delete'), - 'callback_data' => '0:trainer_name:delete' - ] - ]; - }else { - $keys[] = [ - [ - 'text' => getTranslation('trainername_add'), - 'callback_data' => '0:trainer_name:add' - ] - ]; - } -} -$keys[] = [ - [ - 'text' => getTranslation('back'), - 'callback_data' => $mode.':trainer_name:cancel' - ] + if(!empty($user_data['row']['trainername'])) { + $keys[][] = button(getTranslation('switch_display_name'), ['trainer_name', 'a' => 'switch']); + $keys[] = [ + button(getTranslation('trainername_edit'), ['trainer_name', 'a' => 'add']), + button(getTranslation('delete'), ['trainer_name', 'a' => 'delete']) ]; + }else { + $keys[][] = button(getTranslation('trainername_add'), ['trainer_name', 'a' => 'add']); + } +} +$keys[][] = button(getTranslation('back'), ['trainer_name', 'a' => 'cancel']); - // Answer callback. +// Answer callback. answerCallbackQuery($update['callback_query']['id'], $callback_response); // Edit message. edit_message($update, $msg, $keys, false); - - -// Exit. -$dbh = null; -exit(); - -?> diff --git a/mods/trainer_share.php b/mods/trainer_share.php index d2a9a900..c1ac5ca4 100644 --- a/mods/trainer_share.php +++ b/mods/trainer_share.php @@ -1,16 +1,19 @@ accessCheck('trainer-share'); -// Get chat id. -$chat = $data['arg']; +// Get chat object by chat short id +$chatObj = get_config_chat_by_short_id($data['c']); // Get text and keys. $text = show_trainerinfo($update); @@ -20,7 +23,7 @@ $tg_json = array(); // Send the message. -$tg_json[] = send_message($chat, $text, $keys, ['disable_web_page_preview' => 'true'], true, 'trainer'); +$tg_json[] = send_message($chatObj, $text, $keys, ['disable_web_page_preview' => 'true'], true, 'trainer'); // Set callback keys and message $callback_msg = getTranslation('successfully_shared'); @@ -34,6 +37,3 @@ // Telegram multicurl request. curl_json_multi_request($tg_json); - -// Exit. -exit(); diff --git a/mods/trainer_team.php b/mods/trainer_team.php index 02f4a7c2..638d82f5 100644 --- a/mods/trainer_team.php +++ b/mods/trainer_team.php @@ -1,93 +1,60 @@ accessCheck('trainer'); // Confirmation and level -$confirm = $data['id']; -$team = $data['arg']; +$team = $data['t'] ?? ''; // Set the user_id $user_id = $update['callback_query']['from']['id']; // Ask for user level -if($confirm == 0) { +if($team == '') { - // Set keys. - $keys = [ - [ - [ - 'text' => TEAM_B, - 'callback_data' => '1:trainer_team:mystic' - ], - [ - 'text' => TEAM_R, - 'callback_data' => '1:trainer_team:valor' - ], - [ - 'text' => TEAM_Y, - 'callback_data' => '1:trainer_team:instinct' - ] - ], - [ - [ - 'text' => getTranslation('back'), - 'callback_data' => '0:trainer:0' - ], - [ - 'text' => getTranslation('abort'), - 'callback_data' => '0:exit:0' - ] - ] - ]; + // Set keys. + $keys[0][0] = button(TEAM_B, ['trainer_team', 't' => 'mystic']); + $keys[0][1] = button(TEAM_R, ['trainer_team', 't' => 'valor']); + $keys[0][2] = button(TEAM_Y, ['trainer_team', 't' => 'instinct']); + $keys[1][0] = button(getTranslation('back'), 'trainer'); + $keys[1][1] = button(getTranslation('abort'), 'exit'); - // Build message string. - $msg = '' . getTranslation('your_trainer_info') . '' . CR; - $msg .= get_user($user_id) . CR; - $msg .= '' . getTranslation('team_select') . ''; + // Build message string. + $msg = '' . getTranslation('your_trainer_info') . '' . CR; + $msg .= get_user($user_id) . CR; + $msg .= '' . getTranslation('team_select') . ''; - // Build callback message string. - $callback_response = 'OK'; + // Build callback message string. + $callback_response = 'OK'; // Write team to database. -} else if($confirm == 1 && ($team == 'mystic' || $team == 'valor' || $team == 'instinct')) { - // Update the user. - my_query( - " - UPDATE users - SET team = '{$team}' - WHERE user_id = {$user_id} - " - ); +} else if($team == 'mystic' || $team == 'valor' || $team == 'instinct') { + // Update the user. + my_query(' + UPDATE users + SET team = ? + WHERE user_id = ? + ', [$team, $user_id] + ); - // Build message string. - $msg = '' . getTranslation('team_saved') . '' . CR . CR; - $msg .= '' . getTranslation('your_trainer_info') . '' . CR; - $msg .= get_user($user_id) . CR; - - // Build callback message string. - $callback_response = 'OK'; - - // Create the keys. - $keys = [ - [ - [ - 'text' => getTranslation('back'), - 'callback_data' => '0:trainer:0' - ], - [ - 'text' => getTranslation('done'), - 'callback_data' => '0:exit:1' - ] - ] - ]; + // Build message string. + $msg = '' . getTranslation('team_saved') . '' . CR . CR; + $msg .= '' . getTranslation('your_trainer_info') . '' . CR; + $msg .= get_user($user_id) . CR; + // Build callback message string. + $callback_response = 'OK'; + + // Create the keys. + $keys[0][0] = button(getTranslation('back'), 'trainer'); + $keys[0][1] = button(getTranslation('done'), ['exit', 'd' => '1']); } // Answer callback. diff --git a/mods/tutorial.php b/mods/tutorial.php index 088f9db2..a3be6404 100644 --- a/mods/tutorial.php +++ b/mods/tutorial.php @@ -7,110 +7,79 @@ //debug_log($data); // Check access. -bot_access_check($update, 'tutorial'); +$botUser->accessCheck('tutorial'); // Tutorial if(is_file(ROOT_PATH . '/config/tutorial.php')) { - require_once(ROOT_PATH . '/config/tutorial.php'); + require_once(ROOT_PATH . '/config/tutorial.php'); } -$action = $data['arg']; +$action = $data['p'] ?? 1; $user_id = $update['callback_query']['from']['id']; $new_user = new_user($user_id); $tutorial_count = count($tutorial); -if($action == "end") { - answerCallbackQuery($update['callback_query']['id'], "OK!"); - delete_message($update['callback_query']['message']['chat']['id'],$update['callback_query']['message']['message_id']); - if($new_user) { - my_query("UPDATE users SET tutorial = '{$data['id']}' WHERE user_id = '{$user_id}'"); +if($action == 'end') { + answerCallbackQuery($update['callback_query']['id'], 'OK!'); + delete_message($update['callback_query']['message']['chat']['id'],$update['callback_query']['message']['message_id']); + if($new_user) { + my_query('UPDATE users SET tutorial = ? WHERE user_id = ?', [$data['l'], $user_id]); + send_message(create_chat_object([$user_id]), $tutorial_done, []); - send_message($user_id, $tutorial_done, []); - - // Post the user id to external address if specified - if(isset($config->TUTORIAL_COMPLETED_CURL_ADDRESS) && $config->TUTORIAL_COMPLETED_CURL_ADDRESS != "") { - $post_array = [ - "tutorial"=> "OK", - "user_id" => $user_id - ]; - $json = json_encode($post_array); - $URL = $config->TUTORIAL_COMPLETED_CURL_ADDRESS; - $curl = curl_init($URL); + // Post the user id to external address if specified + if(isset($config->TUTORIAL_COMPLETED_CURL_ADDRESS) && $config->TUTORIAL_COMPLETED_CURL_ADDRESS != '') { + $post_array = [ + 'tutorial' => 'OK', + 'user_id' => $user_id + ]; + $json = json_encode($post_array); + $URL = $config->TUTORIAL_COMPLETED_CURL_ADDRESS; + $curl = curl_init($URL); - curl_setopt($curl, CURLOPT_HEADER, false); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-type: application/json")); - curl_setopt($curl, CURLOPT_POST, true); - curl_setopt($curl, CURLOPT_POSTFIELDS, $json); - curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5); - curl_setopt($curl, CURLOPT_TIMEOUT, 10); + curl_setopt($curl, CURLOPT_HEADER, false); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-type: application/json')); + curl_setopt($curl, CURLOPT_POST, true); + curl_setopt($curl, CURLOPT_POSTFIELDS, $json); + curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 5); + curl_setopt($curl, CURLOPT_TIMEOUT, 10); - // Use Proxyserver for curl if configured - if ($config->CURL_USEPROXY && !empty($config->CURL_PROXYSERVER)) { - curl_setopt($curl, CURLOPT_PROXY, $config->CURL_PROXYSERVER); - } + // Use Proxyserver for curl if configured + if ($config->CURL_USEPROXY && !empty($config->CURL_PROXYSERVER)) { + curl_setopt($curl, CURLOPT_PROXY, $config->CURL_PROXYSERVER); + } - // Execute curl request. - $json_response = curl_exec($curl); + // Execute curl request. + $json_response = curl_exec($curl); - // Close connection. - curl_close($curl); - } + // Close connection. + curl_close($curl); } - - - $q = my_query("SELECT level, team FROM users WHERE user_id='{$user_id}' LIMIT 1"); - $row = $q->fetch(); - - if(($row['level']==0 or $row['team']=="" or $row['team']==NULL)) { - $msg = getTranslation("tutorial_no_user_info_set"); - $keys = [ - [ - [ - 'text' => getTranslation("yes"), - 'callback_data' => '0:trainer:0' - ], - [ - 'text' => getTranslation("no"), - 'callback_data' => '0:exit:1' - ] - ] - ]; - send_message($user_id,$msg,$keys); - } - $dbh = null; - exit(); + } + $q = my_query('SELECT level, team FROM users WHERE user_id = ? LIMIT 1', [$user_id]); + $row = $q->fetch(); + if($row['level'] == 0 or $row['team'] == '' or $row['team'] == NULL) { + $msg = getTranslation('tutorial_no_user_info_set'); + $keys[0][0] = button(getTranslation('yes'), 'trainer'); + $keys[0][1] = button(getTranslation('no'), ['exit', 'd' => '1']); + send_message(create_chat_object([$user_id]),$msg,$keys); + } + exit(); +} +if($new_user && isset($tutorial[($action)]['msg_new'])) { + $msg = $tutorial[($action)]['msg_new']; }else { - - if($new_user && isset($tutorial[($action)]['msg_new'])) { - $msg = $tutorial[($action)]['msg_new']; - }else { - $msg = $tutorial[($action)]['msg']; - } - $photo = $tutorial[$action]['photo']; - $keys = []; - if($action > 0) { - $keys = [ - [ - [ - 'text' => getTranslation("back") . " (".($action)."/".($tutorial_count).")", - 'callback_data' => "0:tutorial:".($action-1) - ] - ] - ]; - } - if($action < ($tutorial_count - 1)) { - $keys[0][] = [ - 'text' => getTranslation("next") . " (".($action+2)."/".($tutorial_count).")", - 'callback_data' => "0:tutorial:".($action+1) - ]; - }else { - $keys[0][] = [ - 'text' => getTranslation("done"), - 'callback_data' => $tutorial_grant_level . ":tutorial:end" - ]; - } + $msg = $tutorial[($action)]['msg']; +} +$photo = $tutorial[$action]['photo']; +$keys = []; +if($action > 0) { + $keys[0][] = button(getTranslation('back') . ' ('.($action).'/'.($tutorial_count).')', ['tutorial', 'p' => $action-1]); +} +if($action < ($tutorial_count - 1)) { + $keys[0][] = button(getTranslation('next') . ' ('.($action+2).'/'.($tutorial_count).')', ['tutorial', 'p' => $action+1]); +}else { + $keys[0][] = button(getTranslation('done'), ['tutorial', 'p' => 'end', 'l' => $tutorial_grant_level]); } -answerCallbackQuery($update['callback_query']['id'], "OK!"); -editMessageMedia($update['callback_query']['message']['message_id'], $msg, $photo, $keys, $update['callback_query']['message']['chat']['id'], ['disable_web_page_preview' => 'true']); -?> \ No newline at end of file +answerCallbackQuery($update['callback_query']['id'], 'OK!'); +editMessageMedia($update['callback_query']['message']['message_id'], $msg, $photo, false, $keys, $update['callback_query']['message']['chat']['id'], ['disable_web_page_preview' => 'true']); diff --git a/mods/update_bosses.php b/mods/update_bosses.php index 9cd1cc3e..c69da62b 100644 --- a/mods/update_bosses.php +++ b/mods/update_bosses.php @@ -1,82 +1,91 @@ ENABLE_BOSS_AUTO_UPDATE) { exit; } $levels = $data['id']; -$source = $data ['arg']; -$add_mons = []; +$source = $data['arg']; if($levels != 'scheduled') { - $get_levels = explode(",",$levels); + $get_levels = explode(',',$levels); - // Clear currently saved bosses that were imported with this method or inserted by hand - disable_raid_level($levels); - if($source == 'pogoinfo') { - debug_log('Getting raid bosses from pogoinfo repository now...'); - $link = 'https://raw.githubusercontent.com/ccev/pogoinfo/v2/active/raids.json'; - $data = curl_get_contents($link); - $data = json_decode($data,true); - - debug_log('Processing received ccev pogoinfo raid bosses for each raid level'); - foreach($data as $tier => $tier_pokemon) { - // Process raid level? - if(!in_array($tier,$get_levels)) { - continue; - } - foreach($tier_pokemon as $raid_id_form) { - $dex_id = $raid_id_form['id']; - $dex_form = 0; - if(isset($raid_id_form['temp_evolution_id'])) { - $dex_form = '-'.$raid_id_form['temp_evolution_id']; - }elseif(isset($raid_id_form['form'])) { - $dex_form = $raid_id_form['form']; - }else { - // If no form id is provided, let's check our db for normal form - $query_form_id = my_query("SELECT pokemon_form_id FROM pokemon WHERE pokedex_id='".$dex_id."' and pokemon_form_name='normal' LIMIT 1"); - if($query_form_id->rowCount() == 0) { - // If normal form doesn't exist in our db, use the smallest form id as a fallback - $query_form_id = my_query("SELECT min(pokemon_form_id) as pokemon_form_id FROM pokemon WHERE pokedex_id='".$dex_id."' LIMIT 1"); - } - $result = $query_form_id->fetch(); - $dex_form = $result['pokemon_form_id']; - } + require_once(LOGIC_PATH . '/disable_raid_level.php'); + // Clear currently saved bosses that were imported with this method or inserted by hand + disable_raid_level($levels); + if($source != 'pogoinfo') { + info_log("Invalid argumens supplied to update_bosses!"); + exit(); + } + debug_log('Getting raid bosses from pogoinfo repository now...'); + $link = 'https://raw.githubusercontent.com/ccev/pogoinfo/v2/active/raids.json'; + $data = curl_get_contents($link); + $data = json_decode($data,true); - $add_mons[] = [ - 'pokedex_id' => $dex_id, - 'pokemon_form_id' => $dex_form, - 'raid_level' => $tier, - ]; - } + debug_log('Processing received ccev pogoinfo raid bosses for each raid level'); + $sql_values = ''; + foreach($get_levels as $level) { + if(!isset($data[$level])) continue; + // Process requested levels + foreach($data[$level] as $raid_id_form) { + if(!isset($raid_id_form['id'])) continue; + $dex_id = $raid_id_form['id']; + $dex_form = 0; + if(isset($raid_id_form['temp_evolution_id'])) { + $dex_form = '-'.$raid_id_form['temp_evolution_id']; + }elseif(isset($raid_id_form['form'])) { + $dex_form = $raid_id_form['form']; + }else { + // If no form id is provided, let's check our db for normal form + $query_form_id = my_query('SELECT pokemon_form_id FROM pokemon WHERE pokedex_id = ? and pokemon_form_name=\'normal\' LIMIT 1', [$dex_id]); + if($query_form_id->rowCount() == 0) { + // If normal form doesn't exist in our db, use the smallest form id as a fallback + $query_form_id = my_query('SELECT min(pokemon_form_id) as pokemon_form_id FROM pokemon WHERE pokedex_id = ? LIMIT 1', [$dex_id]); } - }else { - info_log("Invalid argumens supplied to update_bosses!"); - exit(); + $result = $query_form_id->fetch(); + $dex_form = $result['pokemon_form_id']; + } + + $sql_values .= '(\'' . implode("', '", [$dex_id, $dex_form, $level]) . '\'),'; } + } + if($sql_values == '') exit; + $sql_values = rtrim($sql_values, ','); + + $sql = 'INSERT INTO raid_bosses (pokedex_id, pokemon_form_id, raid_level) VALUES ' . $sql_values . ';'; }elseif($levels == 'scheduled') { - require_once(LOGIC_PATH . '/read_upcoming_bosses.php'); - $sql = 'DELETE FROM raid_bosses WHERE scheduled = 1;'; - $sql .= read_upcoming_bosses(true); -}else { - info_log("Invalid argumens supplied to update_bosses!"); - exit(); -} -$count = count($add_mons); -$start = false; -$sql_values = ''; -if($count > 0) { - $sql_cols = implode(", ", array_keys($add_mons[0])); - for($i=0;$i<$count;$i++) { - if($i > 0) $sql_values .= ','; - $sql_values .= "('" . implode("', '", array_values($add_mons[$i])) . "')"; + require_once(LOGIC_PATH . '/read_upcoming_bosses.php'); + $upcoming = read_upcoming_bosses('array', [5,6,7,8,10]); + if(empty($upcoming)) exit; + $idArray = []; + $updateRows = ''; + $updateCount = 0; + // Exclude existing entries from deletion + foreach($upcoming as $row) { + $data = my_query(' + SELECT + id, pokedex_id, pokemon_form_id, date_start, date_end, raid_level + FROM raid_bosses + WHERE pokedex_id = :pokedex_id + AND pokemon_form_id = :pokemon_form_id + AND date_start = :date_start + AND date_end = :date_end + AND scheduled = 1' + ,['pokedex_id' => $row['pokedex_id'], 'pokemon_form_id' => $row['pokemon_form_id'], 'date_start'=>$row['date_start'], 'date_end'=>$row['date_end']]); + $result = $data->fetchAll(PDO::FETCH_COLUMN, 0); + if($data->rowCount() == 0) { + $updateRows .= '(\'' . implode("', '", $row) . '\', \'1\'),'; + $updateCount++; + }else { + $idArray[] = $result[0]; } - $sql = "INSERT INTO raid_bosses (" . $sql_cols . ") VALUES " . $sql_values . ";"; -} + } + $updateRows = rtrim($updateRows, ','); -try { - $query = $dbh->prepare($sql); - $query->execute(); -}catch (PDOException $exception) { - info_log($exception->getMessage()); + $sql = 'DELETE FROM raid_bosses WHERE scheduled = 1 AND id NOT IN ('.implode(',', $idArray).'); '; + if($updateCount > 0) $sql .= 'INSERT INTO raid_bosses (pokedex_id, pokemon_form_id, date_start, date_end, raid_level, scheduled) VALUES ' . $updateRows.';'; +}else { + info_log("Invalid argumens supplied to update_bosses!"); + exit(); } -?> +my_query($sql); diff --git a/mods/vote_can_invite.php b/mods/vote_can_invite.php index ea4c5a66..f67d560f 100644 --- a/mods/vote_can_invite.php +++ b/mods/vote_can_invite.php @@ -1,74 +1,59 @@ prepare( $query_select ); - $statement_select->execute([ - 'raid_id' => $data['id'], - 'user_id' => $update['callback_query']['from']['id'] - ]); - $res = $statement_select->fetch(); - if($statement_select->rowCount() > 0) { - $query = " - UPDATE attendance - SET can_invite = CASE - WHEN can_invite = '0' THEN '1' - ELSE '0' - END, - late = 0, - arrived = 0, - remote = 0, - want_invite = 0, - extra_alien = 0, - extra_in_person = 0 - WHERE raid_id = :raid_id - AND user_id = :user_id - "; - $statement = $dbh->prepare( $query ); - $statement->execute([ - 'raid_id' => $data['id'], - 'user_id' => $update['callback_query']['from']['id'] - ]); - } +$raidId = $data['r']; + +$query_select = my_query(' + SELECT can_invite, CASE WHEN cancel = 1 or raid_done = 1 THEN 1 ELSE 0 END as cancelOrDone + FROM attendance + WHERE raid_id = :raid_id + AND user_id = :user_id + LIMIT 1 + ', + [ + 'raid_id' => $raidId, + 'user_id' => $update['callback_query']['from']['id'], + ]); +$res = $query_select->fetch(); +if($query_select->rowCount() == 0 or $res['cancelOrDone'] == 1) { + // Send vote time first. + send_vote_time_first($update); + exit; } -catch (PDOException $exception) { - - error_log($exception->getMessage()); - $dbh = null; - exit; -} -if($statement_select->rowCount() > 0) { - if($res['can_invite'] == 0) { - $alarm_action = 'can_invite'; - } else { - $alarm_action = 'no_can_invite'; - } - // Send vote response. - require_once(LOGIC_PATH . '/update_raid_poll.php'); - - $tg_json = update_raid_poll($data['id'], false, $update); - - $tg_json = alarm($data['id'],$update['callback_query']['from']['id'], $alarm_action, '', $tg_json); - - $tg_json[] = answerCallbackQuery($update['callback_query']['id'], getTranslation('vote_updated'), true); - - curl_json_multi_request($tg_json); -} else { - // Send vote time first. - send_vote_time_first($update); -} - -$dbh = null; -exit(); +my_query(' + UPDATE attendance + SET can_invite = CASE + WHEN can_invite = 0 THEN 1 + ELSE 0 + END, + late = 0, + arrived = 0, + remote = 0, + want_invite = 0, + extra_alien = 0, + extra_in_person = 0 + WHERE raid_id = :raid_id + AND user_id = :user_id + ', + [ + 'raid_id' => $raidId, + 'user_id' => $update['callback_query']['from']['id'], + ]); +// Send vote response. +require_once(LOGIC_PATH . '/update_raid_poll.php'); + +$tg_json = update_raid_poll($raidId, false, $update); + +$alarm_action = ($res['can_invite'] == 0) ? 'can_invite' : 'no_can_invite'; +$tg_json = alarm($raidId,$update['callback_query']['from']['id'], $alarm_action, '', $tg_json); + +$tg_json[] = answerCallbackQuery($update['callback_query']['id'], getTranslation('vote_updated'), true); + +curl_json_multi_request($tg_json); diff --git a/mods/vote_extra.php b/mods/vote_extra.php index b982686c..4b9c463b 100644 --- a/mods/vote_extra.php +++ b/mods/vote_extra.php @@ -1,19 +1,30 @@ $raidId, + 'userId' => $update['callback_query']['from']['id'], + ] ); // Get the answer. @@ -25,72 +36,74 @@ // Write to log. debug_log($answer); -// User has voted before. -if (!empty($answer)) { - if($data['arg'] == '0') - { - // Reset team extra people. - my_query( - " - UPDATE attendance - SET extra_in_person = 0, - extra_alien = 0 - WHERE raid_id = {$data['id']} - AND user_id = {$update['callback_query']['from']['id']} - " - ); - $tg_json = alarm($data['id'],$update['callback_query']['from']['id'],'extra_alone',$data['arg'], $tg_json); - } else if($answer['can_invite'] == 1 ) { - // People who are only inviting others can't add extras - $msg = getTranslation('vote_status_not_allowed'); +// User has not voted before. +if (empty($answer) or $answer['cancelOrDone'] == 1) { + // Send vote time first. + send_vote_time_first($update); + exit; +} - // Answer the callback. - answerCallbackQuery($update['callback_query']['id'], $msg); +if($act == '0') { + // Reset team extra people. + my_query(' + UPDATE attendance + SET extra_in_person = 0, + extra_alien = 0 + WHERE raid_id = :raidId + AND user_id = :userId + ', + [ + 'raidId' => $raidId, + 'userId' => $update['callback_query']['from']['id'], + ] + ); + $tg_json = alarm($raidId, $update['callback_query']['from']['id'], 'extra_alone', $act, $tg_json); +} else if($answer['can_invite'] == 1 ) { + // People who are only inviting others can't add extras + $msg = getTranslation('vote_status_not_allowed'); - $dbh = null; - exit(); - } else { - // Check if max remote users limit is already reached! - $remote_users = get_remote_users_count($data['id'], $update['callback_query']['from']['id']); - // Skip remote user limit check if user is not attending remotely. - // If they are, check if attend time already has max number of remote users. - // Also prevent user from adding more than max number of remote players even if 'Anytime' is selected - if ($answer['remote'] == 0 or ($remote_users < $config->RAID_REMOTEPASS_USERS_LIMIT && $answer['user_count'] < $config->RAID_REMOTEPASS_USERS_LIMIT)) { - if($answer['want_invite'] == 1 && $data['arg'] == 'alien') { - // Force invite beggars to user extra_in_person even if they vote +alien - $data['arg'] = 'in_person'; - } - $team = 'extra_' . $data['arg']; - // Increase team extra people. - my_query( - " - UPDATE attendance - SET {$team} = {$team}+1 - WHERE raid_id = {$data['id']} - AND user_id = {$update['callback_query']['from']['id']} - " - ); - $tg_json = alarm($data['id'],$update['callback_query']['from']['id'],'extra',$data['arg'], $tg_json); - } else { - // Send max remote users reached. - send_vote_remote_users_limit_reached($update); - $dbh = null; - exit(); - } - } + // Answer the callback. + answerCallbackQuery($update['callback_query']['id'], $msg); - // Send vote response. - require_once(LOGIC_PATH . '/update_raid_poll.php'); + exit; +} else { + // Check if max remote users limit is already reached! + $remote_users = get_remote_users_count($raidId, $update['callback_query']['from']['id']); + // Skip remote user limit check if user is not attending remotely. + // If they are, check if attend time already has max number of remote users. + if (($answer['remote'] == 1 or $act == 'alien') && ($remote_users >= $config->RAID_REMOTEPASS_USERS_LIMIT or $answer['user_count'] >= $config->RAID_REMOTEPASS_USERS_LIMIT)) { + // Send max remote users reached. + send_vote_remote_users_limit_reached($update); + exit; + } + // Force invite beggars to user extra_in_person even if they vote +alien + if($answer['want_invite'] == 1 && $act == 'alien') $act = 'in_person'; - $tg_json = update_raid_poll($data['id'], false, $update, $tg_json); + if(!in_array($act, ['in_person', 'alien'])) { + error_log('Invalid vote variable: ' . $act); + exit; + } + $team = 'extra_' . $act; + // Increase team extra people. + my_query(' + UPDATE attendance + SET ' . $team . ' = ' . $team . ' + 1 + WHERE raid_id = :raidId + AND user_id = :userId + ', + [ + 'raidId' => $raidId, + 'userId' => $update['callback_query']['from']['id'], + ] + ); + $tg_json = alarm($raidId, $update['callback_query']['from']['id'], 'extra', $act, $tg_json); +} - $tg_json[] = answerCallbackQuery($update['callback_query']['id'], getTranslation('vote_updated'), true); +// Send vote response. +require_once(LOGIC_PATH . '/update_raid_poll.php'); - curl_json_multi_request($tg_json); -} else { - // Send vote time first. - send_vote_time_first($update); -} +$tg_json = update_raid_poll($raidId, false, $update, $tg_json); + +$tg_json[] = answerCallbackQuery($update['callback_query']['id'], getTranslation('vote_updated'), true); -$dbh = null; -exit(); +curl_json_multi_request($tg_json); diff --git a/mods/vote_invite.php b/mods/vote_invite.php index 1aad9f0b..c0dc0392 100644 --- a/mods/vote_invite.php +++ b/mods/vote_invite.php @@ -7,22 +7,21 @@ //debug_log($data); // Update team in users table. -my_query( - " - UPDATE attendance - SET invite = CASE - WHEN invite = '0' THEN '1' - ELSE '0' - END - WHERE raid_id = {$data['id']} - AND user_id = {$update['callback_query']['from']['id']} - " +my_query(' + UPDATE attendance + SET invite = CASE + WHEN invite = 0 THEN 1 + ELSE 0 + END + WHERE raid_id = ? + AND user_id = ? + ', [$data['r'], $update['callback_query']['from']['id']] ); // Send vote response. require_once(LOGIC_PATH . '/update_raid_poll.php'); -$tg_json = update_raid_poll($data['id'], false, $update); +$tg_json = update_raid_poll($data['r'], false, $update); $tg_json[] = answerCallbackQuery($update['callback_query']['id'], getTranslation('vote_updated'), true); diff --git a/mods/vote_level.php b/mods/vote_level.php index b8a3d281..cd923c93 100644 --- a/mods/vote_level.php +++ b/mods/vote_level.php @@ -1,61 +1,56 @@ TRAINER_MAX_LEVEL} - " - ); +if ($level == 'up') { + // Increase users level. + my_query(' + UPDATE users + SET level = IF(level = 0, 30, level+1) + WHERE user_id = ? + AND level < ' . $config->TRAINER_MAX_LEVEL . ' + ', [$update['callback_query']['from']['id']] + ); } // Down-vote. -if ($action == 'down') { - // Decrease users level. - my_query( - " - UPDATE users - SET level = level-1 - WHERE user_id = {$update['callback_query']['from']['id']} - AND level > 5 - " - ); +if ($level == 'down') { + // Decrease users level. + my_query(' + UPDATE users + SET level = level-1 + WHERE user_id = ? + AND level > 5 + ', [$update['callback_query']['from']['id']] + ); } // Message coming from raid or trainer info? -if($data['id'] == 'trainer') { - if($action == 'hide') { - // Send trainer info update. - send_trainerinfo($update, false); - } else if($action == 'show') { - // Send trainer info update. - send_trainerinfo($update, true); - } else { - // Send trainer info update. - send_trainerinfo($update, true); - } -} else { - // Send vote response. - require_once(LOGIC_PATH . '/update_raid_poll.php'); - - $tg_json = update_raid_poll($data['id'], false, $update); - - $tg_json[] = answerCallbackQuery($update['callback_query']['id'], getTranslation('vote_updated'), true); - - curl_json_multi_request($tg_json); +if($action == 'trainer') { + if($show == 'hide') { + // Send trainer info update. + send_trainerinfo($update, false); + } + // Send trainer info update. + send_trainerinfo($update, true); + exit; } +// Send vote response. +require_once(LOGIC_PATH . '/update_raid_poll.php'); -exit(); +$tg_json = update_raid_poll($data['r'], false, $update); + +$tg_json[] = answerCallbackQuery($update['callback_query']['id'], getTranslation('vote_updated'), true); + +curl_json_multi_request($tg_json); diff --git a/mods/vote_pokemon.php b/mods/vote_pokemon.php index 7cd8c7d1..cc713dc5 100644 --- a/mods/vote_pokemon.php +++ b/mods/vote_pokemon.php @@ -1,181 +1,150 @@ fetch()) { - $atts[] = $row; - $count = $count + 1; -} +$atts = $rs->fetchAll(); +$count = $rs->rowCount(); // Write to log. debug_log($atts); // User has voted before. -if(!empty($atts)) { - // Any pokemon? - if($data['arg'] == 0) { - // Update attendance. - my_query( - " - UPDATE attendance - SET pokemon = '{$data['arg']}' - WHERE raid_id = {$data['id']} - AND user_id = {$update['callback_query']['from']['id']} - " +if(empty($atts)) { + // Send vote time first. + send_vote_time_first($update); + exit; +} +// Any pokemon? +if($pokemon == 0) { + // Delete any attendances except the first one. + my_query(' + DELETE FROM attendance + WHERE id NOT IN ( + SELECT * FROM ( + SELECT MIN(id) + FROM attendance + WHERE raid_id = :raidId + AND user_id = :userId + ) AS AVOID_MYSQL_ERROR_1093 + ) + AND raid_id = :raidId + AND user_id = :userId + ', ['raidId' => $raidId, 'userId' => $update['callback_query']['from']['id']] + ); + + // Update attendance. + my_query(' + UPDATE attendance + SET pokemon = ? + WHERE raid_id = ? + AND user_id = ? + ', [$pokemon, $raidId, $update['callback_query']['from']['id']] + ); + + // Send alarm + $tg_json = alarm($raidId,$update['callback_query']['from']['id'],'pok_individual',$pokemon); +} else { + // Init found and count. + $found = false; + + // Loop thru attendances + foreach($atts as $att_row => $att_data) { + // Remove vote for specific pokemon + if($att_data['pokemon'] == $pokemon) { + // Is it the only vote? Update to "Any raid boss" instead of deleting it! + if($count == 1) { + my_query(' + UPDATE attendance + SET pokemon = 0 + WHERE raid_id = ? + AND user_id = ? + ', [$raidId, $update['callback_query']['from']['id']] ); - - // Delete any attendances except the first one. - my_query( - " + // Other votes are still there, delete this one! + } else { + my_query(' DELETE FROM attendance - WHERE id NOT IN ( - SELECT * FROM ( - SELECT MIN(id) - FROM attendance - WHERE raid_id = {$data['id']} - AND user_id = {$update['callback_query']['from']['id']} - ) AS AVOID_MYSQL_ERROR_1093 - ) - AND raid_id = {$data['id']} - AND user_id = {$update['callback_query']['from']['id']} - " + WHERE raid_id = ? + AND user_id = ? + AND pokemon = ? + ', [$raidId, $update['callback_query']['from']['id'], $pokemon] ); + } + // Send alarm + $tg_json = alarm($raidId,$update['callback_query']['from']['id'],'pok_cancel_individual',$pokemon); - // Send alarm - $tg_json = alarm($data['id'],$update['callback_query']['from']['id'],'pok_individual',$data['arg']); - } else { - // Init found and count. - $found = false; - - // Loop thru attendances - foreach($atts as $att_row => $att_data) { - // Remove vote for specific pokemon - if($att_data['pokemon'] == $data['arg']) { - // Is it the only vote? Update to "Any raid boss" instead of deleting it! - if($count == 1) { - my_query( - " - UPDATE attendance - SET pokemon = '0' - WHERE raid_id = {$data['id']} - AND user_id = {$update['callback_query']['from']['id']} - " - ); - // Other votes are still there, delete this one! - } else { - my_query( - " - DELETE FROM attendance - WHERE raid_id = {$data['id']} - AND user_id = {$update['callback_query']['from']['id']} - AND pokemon = '{$data['arg']}' - " - ); - } - // Send alarm - $tg_json = alarm($data['id'],$update['callback_query']['from']['id'],'pok_cancel_individual',$data['arg']); - - // Update count. - $count = $count - 1; - - // Found and break. - $found = true; - break; - } - } - - // Not found? Insert! - if(!$found) { - // Send alarm - $tg_json = alarm($data['id'],$update['callback_query']['from']['id'],'pok_individual',$data['arg']); - - // Insert vote. - my_query( - " - INSERT INTO attendance - ( - user_id, - raid_id, - attend_time, - extra_in_person, - extra_alien, - arrived, - raid_done, - cancel, - late, - remote, - invite, - pokemon, - alarm, - want_invite, - can_invite - ) - VALUES( - '{$atts[0]['user_id']}', - '{$atts[0]['raid_id']}', - '{$atts[0]['attend_time']}', - '{$atts[0]['extra_in_person']}', - '{$atts[0]['extra_alien']}', - '{$atts[0]['arrived']}', - '{$atts[0]['raid_done']}', - '{$atts[0]['cancel']}', - '{$atts[0]['late']}', - '{$atts[0]['remote']}', - '{$atts[0]['invite']}', - '{$data['arg']}', - '{$atts[0]['alarm']}', - '{$atts[0]['want_invite']}', - '{$atts[0]['can_invite']}' - ) - " - ); - - // Update counter. - $count = $count + 1; - } - - // Delete "Any raid boss" vote if count is larger than 0 - if($count > 0) { - my_query( - " - DELETE FROM attendance - WHERE raid_id = {$data['id']} - AND user_id = {$update['callback_query']['from']['id']} - AND pokemon = '0' - " - ); - } + // Update count. + $count = $count - 1; + + // Found and break. + $found = true; + break; + } + } + + // Not found? Insert! + if(!$found) { + // Send alarm + $tg_json = alarm($raidId,$update['callback_query']['from']['id'],'pok_individual',$pokemon); + + $keys = $values = ''; + $binds = []; + foreach($atts[0] as $key => $value) { + if($key == 'id') continue; + $keys .= $key . ','; + $values .= '?,'; + $binds[] = ($key == 'pokemon') ? $pokemon : $value; } + $keys = rtrim($keys, ','); + $values = rtrim($values, ','); + // Insert vote. + my_query(' + INSERT INTO attendance (' . $keys . ') + VALUES (' . $values . ') + ', $binds + ); + + // Update counter. + $count = $count + 1; + } + + // Delete "Any raid boss" vote if count is larger than 0 + if($count > 1) { + my_query(' + DELETE FROM attendance + WHERE raid_id = ? + AND user_id = ? + AND pokemon = 0 + ', [$raidId, $update['callback_query']['from']['id']] + ); + } +} - require_once(LOGIC_PATH . '/update_raid_poll.php'); +require_once(LOGIC_PATH . '/update_raid_poll.php'); - $tg_json = update_raid_poll($data['id'], false, $update, $tg_json); +$tg_json = update_raid_poll($raidId, false, $update, $tg_json); - $tg_json[] = answerCallbackQuery($update['callback_query']['id'], getTranslation('vote_updated'), true); +$tg_json[] = answerCallbackQuery($update['callback_query']['id'], getTranslation('vote_updated'), true); - curl_json_multi_request($tg_json); -} else { - // Send vote time first. - send_vote_time_first($update); -} +curl_json_multi_request($tg_json); exit(); diff --git a/mods/vote_refresh.php b/mods/vote_refresh.php index 315cbdce..cfd904d6 100644 --- a/mods/vote_refresh.php +++ b/mods/vote_refresh.php @@ -1,11 +1,10 @@ $raidId, + 'userId' => $update['callback_query']['from']['id'], + ] ); // Get remote value. $remote = $rs->fetch(); -$remote_status = isset($remote['remote']) ? $remote['remote'] : 0; -$user_remote_count = isset($remote['user_count']) ? $remote['user_count'] : 0; -// Check if max remote users limit is already reached! -$remote_users = get_remote_users_count($data['id'], $update['callback_query']['from']['id']); +if($rs->rowCount() == 0 or $remote['cancelOrDone'] == 1) { + // Send vote time first. + send_vote_time_first($update); + exit; +} -if($rs->rowCount() > 0) { - // Ignore max users reached when switching from remote to local otherwise check if max users reached? - if ($remote_users + $user_remote_count <= $config->RAID_REMOTEPASS_USERS_LIMIT || $remote_status == 1) { - // Update users table. - my_query( - " - UPDATE attendance - SET remote = CASE - WHEN remote = '0' THEN '1' - ELSE '0' - END, - want_invite = 0, - can_invite = 0 - WHERE raid_id = {$data['id']} - AND user_id = {$update['callback_query']['from']['id']} - " - ); +$remote_status = $remote['remote']; - if($remote_status == 0) { - $tg_json = alarm($data['id'],$update['callback_query']['from']['id'],'remote'); - } else { - $tg_json = alarm($data['id'],$update['callback_query']['from']['id'],'no_remote'); - } +// Check if max remote users limit is already reached! +$remote_users = get_remote_users_count($raidId, $update['callback_query']['from']['id']); +// Ignore max users reached when switching from remote to local otherwise check if max users reached? +if($remote_status == 0 && $config->RAID_REMOTEPASS_USERS_LIMIT < $remote_users + $remote['user_count']) { + // Send max remote users reached. + send_vote_remote_users_limit_reached($update); + exit; +} +// Update users table. +my_query(' + UPDATE attendance + SET remote = CASE + WHEN remote = 0 THEN 1 + ELSE 0 + END, + want_invite = 0, + can_invite = 0 + WHERE raid_id = :raidId + AND user_id = :userId + ', + [ + 'raidId' => $raidId, + 'userId' => $update['callback_query']['from']['id'], + ] +); - // Send vote response. - require_once(LOGIC_PATH . '/update_raid_poll.php'); +$tg_json = alarm($raidId, $update['callback_query']['from']['id'], ($remote_status == 0 ? 'remote' : 'no_remote')); - $tg_json = update_raid_poll($data['id'], false, $update, $tg_json); +// Send vote response. +require_once(LOGIC_PATH . '/update_raid_poll.php'); - $tg_json[] = answerCallbackQuery($update['callback_query']['id'], getTranslation('vote_updated'), true); +$tg_json = update_raid_poll($raidId, false, $update, $tg_json); - curl_json_multi_request($tg_json); - } else { - // Send max remote users reached. - send_vote_remote_users_limit_reached($update); - } -} else { - // Send vote time first. - send_vote_time_first($update); -} +$tg_json[] = answerCallbackQuery($update['callback_query']['id'], getTranslation('vote_updated'), true); -exit(); +curl_json_multi_request($tg_json); diff --git a/mods/vote_status.php b/mods/vote_status.php index 71e41ab3..86ca0ba4 100644 --- a/mods/vote_status.php +++ b/mods/vote_status.php @@ -1,25 +1,28 @@ $update['callback_query']['from']['id'], 'raidId' => $raidId] ); // Get the answer. @@ -32,81 +35,71 @@ $tg_json = array(); // Get status to update -$status = $data['arg']; +$status = $data['a']; // Make sure user has voted before. -if (!empty($answer)) { - // Prevent invite beggars from voting late or arrived - if(!($answer['want_invite'] == 1 && ($status == 'late' || $status == 'arrived'))) { - // Update attendance. - if($status == 'alarm') { - // Enable / Disable alarm - my_query( - " - UPDATE attendance - SET alarm = CASE - WHEN alarm = '0' THEN '1' - ELSE '0' - END - WHERE raid_id = {$data['id']} - AND user_id = {$update['callback_query']['from']['id']} - " - ); - $new_alarm_value = $answer['alarm'] = 0 ? 1 : 0; - // Inform User about change - sendAlertOnOffNotice($data['id'], $update['callback_query']['from']['id'], $new_alarm_value); - } else { - // All other status-updates are using the short query - my_query( - " - UPDATE attendance - SET arrived = 0, - raid_done = 0, - cancel = 0, - late = 0, - $status = 1 - WHERE raid_id = {$data['id']} - AND user_id = {$update['callback_query']['from']['id']} - " - ); - if($status == 'raid_done' or $status == 'cancel') { - if($status == 'cancel') $tg_json = alarm($data['id'],$update['callback_query']['from']['id'],'status',$status, $tg_json); - // If the gym is a temporary remote raid gym and raid creator voted for done, send message asking for raid deletion - if($answer['is_remote_gym'] == '1' && $answer['user_is_creator']) { - $keys = [[ - [ - 'text' => getTranslation('yes'), - 'callback_data' => $data['id'].':end_remote_raid:0' - ], - [ - 'text' => getTranslation('no'), - 'callback_data' => '0:exit:0' - ], - ]]; - if($status == 'raid_done') $msg = getTranslation("delete_remote_raid_done"); - else if($status == 'cancel') $msg = getTranslation("delete_remote_raid_cancel"); - $tg_json[] = send_message($update['callback_query']['from']['id'], $msg, $keys, false, true); - } - }elseif($status != 'arrived') { - $tg_json = alarm($data['id'],$update['callback_query']['from']['id'],'status',$status, $tg_json); - } - } +if (empty($answer)) { + // Send vote time first. + send_vote_time_first($update); + exit; +} +// Prevent invite beggars from voting late or arrived +if(($answer['want_invite'] == 1 && ($status == 'late' || $status == 'arrived'))) { + $msg = getTranslation('vote_status_not_allowed'); + answerCallbackQuery($update['callback_query']['id'], $msg); + exit; +} +// Update attendance. +if($status == 'alarm') { + // Enable / Disable alarm + my_query(' + UPDATE attendance + SET alarm = CASE + WHEN alarm = 0 THEN 1 + ELSE 0 + END + WHERE raid_id = ? + AND user_id = ? + ', [$raidId, $update['callback_query']['from']['id']] + ); + $new_alarm_value = $answer['alarm'] = 0 ? 1 : 0; + // Inform User about change + sendAlertOnOffNotice($raidId, $update['callback_query']['from']['id'], $new_alarm_value); +} else { + // All other status-updates are using the short query + my_query(' + UPDATE attendance + SET arrived = 0, + raid_done = 0, + cancel = 0, + late = 0, + ' . $status . ' = 1 + WHERE raid_id = ? + AND user_id = ? + ', [$raidId, $update['callback_query']['from']['id']] + ); + if($status == 'raid_done' or $status == 'cancel') { + if($status == 'cancel') $tg_json = alarm($raidId,$update['callback_query']['from']['id'],'status',$status, $tg_json); + // If the gym is a temporary remote raid gym and raid creator voted for done, send message asking for raid deletion + if($answer['is_remote_gym'] == '1' && $answer['user_is_creator']) { + $keys[0][0] = button(getTranslation('yes'), ['end_remote_raid', 'r' => $raidId]); + $keys[0][1] = button(getTranslation('no'), 'exit'); + if($status == 'raid_done') $msg = getTranslation("delete_remote_raid_done"); + else if($status == 'cancel') $msg = getTranslation("delete_remote_raid_cancel"); + $tg_json[] = send_message(create_chat_object([$update['callback_query']['from']['id']]), $msg, $keys, false, true); + } + }elseif($status != 'arrived') { + $tg_json = alarm($raidId,$update['callback_query']['from']['id'],'status',$status, $tg_json); + } +} - // Send vote response. - require_once(LOGIC_PATH . '/update_raid_poll.php'); +// Send vote response. +require_once(LOGIC_PATH . '/update_raid_poll.php'); - $tg_json = update_raid_poll($data['id'], false, $update, $tg_json); +$tg_json = update_raid_poll($raidId, false, $update, $tg_json); - $tg_json[] = answerCallbackQuery($update['callback_query']['id'], getTranslation('vote_updated'), true); +$tg_json[] = answerCallbackQuery($update['callback_query']['id'], getTranslation('vote_updated'), true); - curl_json_multi_request($tg_json); - }else { - $msg = getTranslation('vote_status_not_allowed'); - answerCallbackQuery($update['callback_query']['id'], $msg); - } -} else { - // Send vote time first. - send_vote_time_first($update); -} +curl_json_multi_request($tg_json); exit(); diff --git a/mods/vote_team.php b/mods/vote_team.php index d35590df..a5b475d0 100644 --- a/mods/vote_team.php +++ b/mods/vote_team.php @@ -1,48 +1,47 @@ fetch(); -// Get number of participants, if 0 -> raid has no participant at all -$count_att = $answer_count['count']; -// Write to log. -debug_log($answer_count, 'Anyone Voted: '); +$raidId = $data['r']; + +$vote_time = $data['t'] ?? 0; +// Raid anytime? +$attend_time_save = $attend_time_compare = ANYTIME; +if($vote_time != 0) { + // Normal raid time - convert data arg to UTC time. + $dt = new DateTime(); + $dt_attend = $dt->createFromFormat('YmdHis', $vote_time, new DateTimeZone('UTC')); + $attend_time_compare = $dt_attend->format('Y-m-d H:i:s'); + $attend_time_save = $dt_attend->format('Y-m-d H:i') . ':00'; + + // Get current time. + $now = new DateTime('now', new DateTimeZone('UTC')); + $now = $now->format('Y-m-d H:i') . ':00'; +} // Request Raid and Gym - Infos -$raid = get_raid($data['id']); +$raid = get_raid($raidId); -// Check if the user has voted for this raid before. -if($count_att > 0){ - $rs = my_query( - " - SELECT user_id, remote, (1 + extra_in_person + extra_alien) as user_count - FROM attendance - WHERE raid_id = {$data['id']} - AND user_id = {$update['callback_query']['from']['id']} - LIMIT 1 - " - ); - - // Get the answer. - $answer = $rs->fetch(); - - // Write to log. - debug_log($answer); -}else{ - $answer = []; +// Vote time in the future? +if($attend_time_compare != ANYTIME && $now >= $attend_time_compare && $raid['event_vote_key_mode'] != 1) { + // Send vote time first. + send_vote_time_future($update); + exit; } -$tg_json = []; +// Check if the user has voted for this raid before. +$rs = my_query(' + SELECT count(attendance.attend_time) AS count, userInfo.* + FROM attendance + LEFT JOIN ( + SELECT raid_id, user_id, remote, attend_time, extra_alien, + (CASE WHEN remote = 1 THEN 1 + extra_in_person ELSE 0 END + extra_alien) as user_count, + CASE WHEN cancel = 1 or raid_done = 1 THEN 1 ELSE 0 END as cancelOrDone + FROM attendance + WHERE raid_id = :raidId + AND user_id = :userId + LIMIT 1 + ) as userInfo + ON userInfo.raid_id = attendance.raid_id + WHERE attendance.raid_id = :raidId + ', + [ + 'raidId' => $raidId, + 'userId' => $update['callback_query']['from']['id'], + ] +); -$vote_time = $data['arg']; -// Raid anytime? -if($vote_time == 0) { - // Raid anytime. - $attend_time_save = $attend_time_compare = ANYTIME; -} else { - // Normal raid time - convert data arg to UTC time. - $dt = new DateTime(); - $dt_attend = $dt->createFromFormat('YmdHis', $vote_time, new DateTimeZone('UTC')); - $attend_time_compare = $dt_attend->format('Y-m-d H:i:s'); - $attend_time_save = $dt_attend->format('Y-m-d H:i') . ':00'; - - // Get current time. - $now = new DateTime('now', new DateTimeZone('UTC')); - $now = $now->format('Y-m-d H:i') . ':00'; -} +// Get the answer. +$answer = $rs->fetch(); +// Get number of participants, if 0 -> raid has no participant at all +$count_att = $answer['count'] ?? 0; -// Vote time in the future or Raid anytime? -if($now <= $attend_time_compare || $vote_time == 0) { - // If user is attending remotely, get the number of remote users already attending - if (!is_array($answer) or !in_array('remote', $answer) or $answer['remote'] == 0){ - $remote_users = 0; - } else { - $remote_users = get_remote_users_count($data['id'], $update['callback_query']['from']['id'], $attend_time); - } - // Check if max remote users limit is already reached, unless voting for 'Anytime' - if ((!empty($answer) && ($answer['remote'] == 0 || $remote_users + $answer['user_count'])) <= $config->RAID_REMOTEPASS_USERS_LIMIT || $vote_time == 0) { - // User has voted before. - if (!empty($answer)) { - // Update attendance. - $update_pokemon_sql = ''; - if(!in_array($raid['pokemon'], $eggs)) { - // If raid egg has hatched - // -> clean up attendance table from votes for other pokemon - // -> leave one entry remaining and set the pokemon to 0 there - my_query(" - DELETE a1 - FROM attendance a1 - INNER JOIN attendance a2 - WHERE a1.id < a2.id - AND a1.user_id = a2.user_id - AND a2.raid_id = {$raid['id']} - AND a1.raid_id = {$raid['id']} - "); - $update_pokemon_sql = 'pokemon = \'0\','; - } - my_query( - " - UPDATE attendance - SET attend_time = '{$attend_time_save}', - cancel = 0, - arrived = 0, - raid_done = 0, - {$update_pokemon_sql} - late = 0 - WHERE raid_id = {$data['id']} - AND user_id = {$update['callback_query']['from']['id']} - " - ); - $tg_json = alarm($data['id'],$update['callback_query']['from']['id'],'change_time', $attend_time_save, $tg_json); - - // User has not voted before. - } else { - $q_user = my_query("SELECT auto_alarm FROM users WHERE user_id ='" . $update['callback_query']['from']['id'] . "' LIMIT 1"); - $user_alarm = $q_user->fetch()['auto_alarm']; - if($config->RAID_AUTOMATIC_ALARM) { - $set_alarm = true; - }else { - $set_alarm = ($user_alarm == 1 ? true : false); - } - // Create attendance. - // Save attandence to DB + Set Auto-Alarm on/off according to config - $insert_sql="INSERT INTO attendance SET - raid_id = :raid_id, - user_id = :user_id, - attend_time = :attend_time, - alarm = :alarm"; - $dbh->prepare($insert_sql)->execute([ - 'raid_id' => $data['id'], - 'user_id' => $update['callback_query']['from']['id'], - 'attend_time' => $attend_time_save, - 'alarm' => ($set_alarm ? 1 : 0) - ]); - // Send Alarm. - $tg_json = alarm($data['id'],$update['callback_query']['from']['id'],'new_att', $attend_time_save, $tg_json); - - // Enable alerts message. -> only if alert is on - if($set_alarm) { - // Inform User about active alert - sendAlertOnOffNotice($data['id'], $update['callback_query']['from']['id'], 1, $raid); - } - } - // Check if RAID has no participants AND Raid should be shared to another chat at first participant - // AND target chat was set in config AND Raid was not shared to target chat before - if($count_att == 0 && $config->SHARE_AFTER_ATTENDANCE && !empty($config->SHARE_CHATS_AFTER_ATTENDANCE)){ - // Check if Raid has been posted to target chat - $rs_chann = my_query( - " - SELECT * - FROM cleanup - WHERE raid_id = {$data['id']} - AND chat_id = {$config->SHARE_CHATS_AFTER_ATTENDANCE} - "); - // IF raid was not shared to target chat, we want to share it - if ($rs_chann->rowCount() == 0) { - // Send the message. - require_once(LOGIC_PATH . '/send_raid_poll.php'); - $tg_json = send_raid_poll($data['id'], $config->SHARE_CHATS_AFTER_ATTENDANCE, $raid, $tg_json); - } - } - } else { - // Send max remote users reached. - send_vote_remote_users_limit_reached($update); - $dbh = null; - exit(); - } +// Write to log. +debug_log($count_att, 'Anyone Voted: '); +debug_log($answer); + +$tg_json = []; +// User has voted before. +if ($answer['user_count'] != NULL) { + // Exit if user is voting for the same time again unless they are done/canceled + if(array_key_exists('attend_time', $answer) && $answer['cancelOrDone'] == 0 && $attend_time_save == $answer['attend_time']) { + answerCallbackQuery($update['callback_query']['id'], 'OK'); + exit; + } + // If user voted something else than 'Anytime', get the number of remote users already attending + $remote_users = ($vote_time == 0) ? 0 : get_remote_users_count($raidId, $update['callback_query']['from']['id'], $attend_time_save); + + // Check if max remote users limit is already reached, unless voting for 'Anytime' + if(($answer['remote'] != 0 or $answer['extra_alien'] > 0) && $vote_time != 0 && $config->RAID_REMOTEPASS_USERS_LIMIT < ($remote_users + $answer['user_count'])) { + // Send max remote users reached. + send_vote_remote_users_limit_reached($update); + exit; + } + + // Update attendance. + $update_pokemon_sql = ''; + if(!in_array($raid['pokemon'], EGGS)) { + // If raid egg has hatched + // -> clean up attendance table from votes for other pokemon + // -> leave one entry remaining and set the pokemon to 0 there + my_query(' + DELETE a1 + FROM attendance a1 + INNER JOIN attendance a2 + WHERE a1.id < a2.id + AND a1.user_id = a2.user_id + AND a2.raid_id = :raid_id + AND a1.raid_id = :raid_id + ', + [ + 'raid_id' => $raid['id'], + ]); + $update_pokemon_sql = 'pokemon = \'0\','; + } + my_query(' + UPDATE attendance + SET attend_time = :attendTimeSave, + cancel = 0, + arrived = 0, + raid_done = 0, + ' . $update_pokemon_sql . ' + late = 0 + WHERE raid_id = :raidId + AND user_id = :userId + ', + [ + 'attendTimeSave' => $attend_time_save, + 'raidId' => $raidId, + 'userId' => $update['callback_query']['from']['id'], + ] + ); + $tg_json = alarm($raidId, $update['callback_query']['from']['id'], 'change_time', $attend_time_save, $tg_json); + +// User has not voted before. } else { - // Send vote time first. - send_vote_time_future($update); + $q_user = my_query("SELECT auto_alarm FROM users WHERE user_id ='" . $update['callback_query']['from']['id'] . "' LIMIT 1"); + $set_alarm = (!$config->RAID_AUTOMATIC_ALARM) ? $q_user->fetch()['auto_alarm'] : 1; + + // Create attendance. + // Save attandence to DB + Set Auto-Alarm on/off according to config + $insert_sql = my_query(' + INSERT INTO attendance SET + raid_id = :raidId, + user_id = :userId, + attend_time = :attendTime, + alarm = :alarm + ', + [ + 'raidId' => $raidId, + 'userId' => $update['callback_query']['from']['id'], + 'attendTime' => $attend_time_save, + 'alarm' => $set_alarm, + ]); + // Send Alarm. + $tg_json = alarm($raidId,$update['callback_query']['from']['id'],'new_att', $attend_time_save, $tg_json); + + // Enable alerts message. -> only if alert is on + if($set_alarm == 1) { + // Inform User about active alert + sendAlertOnOffNotice($raidId, $update['callback_query']['from']['id'], 1, $raid); + } + + // Check if RAID has no participants AND Raid should be shared to another chat at first participant + // AND target chat was set in config AND Raid was not shared to target chat before + // Start share_chats backwards compatibility + if(!isset($config->CHATS_SHARE)) { + if($count_att == 0 && $config->SHARE_AFTER_ATTENDANCE && !empty($config->SHARE_CHATS_AFTER_ATTENDANCE)){ + // Send the message. + require_once(LOGIC_PATH . '/send_raid_poll.php'); + $chats = [create_chat_object([$config->SHARE_CHATS_AFTER_ATTENDANCE])]; + $tg_json = send_raid_poll($raidId, $chats, $raid, $tg_json); + } + // End share_chats backwards compatibility + }else { + if($count_att == 0 && isset($config->CHATS_SHARE['after_attendance'][0]['id'])){ + // Send the message. + require_once(LOGIC_PATH . '/send_raid_poll.php'); + $tg_json = send_raid_poll($raidId, $config->CHATS_SHARE['after_attendance'], $raid, $tg_json); + } + } } // Send vote response. require_once(LOGIC_PATH . '/update_raid_poll.php'); -$tg_json = update_raid_poll($data['id'], $raid, $update, $tg_json); +$tg_json = update_raid_poll($raidId, $raid, $update, $tg_json); $tg_json[] = answerCallbackQuery($update['callback_query']['id'], getTranslation('vote_updated'), true); curl_json_multi_request($tg_json); - -$dbh = null; -exit(); \ No newline at end of file diff --git a/mods/vote_want_invite.php b/mods/vote_want_invite.php index 6599220d..0ac4bfef 100644 --- a/mods/vote_want_invite.php +++ b/mods/vote_want_invite.php @@ -1,73 +1,58 @@ prepare( $query_select ); - $statement_select->execute([ - 'raid_id' => $data['id'], - 'user_id' => $update['callback_query']['from']['id'] - ]); - $res = $statement_select->fetch(); - if($statement_select->rowCount() > 0) { - $query = " - UPDATE attendance - SET want_invite = CASE - WHEN want_invite = '0' THEN '1' - ELSE '0' - END, - late = 0, - arrived = 0, - remote = 0, - extra_alien = 0, - can_invite = 0 - WHERE raid_id = :raid_id - AND user_id = :user_id - "; - $statement = $dbh->prepare( $query ); - $statement->execute([ - 'raid_id' => $data['id'], - 'user_id' => $update['callback_query']['from']['id'] - ]); - } +$raidId = $data['r']; + +$query_select = my_query(' + SELECT want_invite, CASE WHEN cancel = 1 or raid_done = 1 THEN 1 ELSE 0 END as cancelOrDone + FROM attendance + WHERE raid_id = :raid_id + AND user_id = :user_id + LIMIT 1 + ', + [ + 'raid_id' => $raidId, + 'user_id' => $update['callback_query']['from']['id'], + ]); +$res = $query_select->fetch(); +if($query_select->rowCount() == 0 or $res['cancelOrDone'] == 1) { + // Send vote time first. + send_vote_time_first($update); + exit; } -catch (PDOException $exception) { - - error_log($exception->getMessage()); - $dbh = null; - exit; -} -if($statement_select->rowCount() > 0) { - if($res['want_invite'] == 0) { - $alarm_action = 'want_invite'; - } else { - $alarm_action = 'no_want_invite'; - } - // Send vote response. - require_once(LOGIC_PATH . '/update_raid_poll.php'); - - $tg_json = update_raid_poll($data['id'], false, $update); - - $tg_json = alarm($data['id'],$update['callback_query']['from']['id'],$alarm_action, '', $tg_json); - - $tg_json[] = answerCallbackQuery($update['callback_query']['id'], getTranslation('vote_updated'), true); - - curl_json_multi_request($tg_json); -} else { - // Send vote time first. - send_vote_time_first($update); -} - -$dbh = null; -exit(); +my_query(' + UPDATE attendance + SET want_invite = CASE + WHEN want_invite = 0 THEN 1 + ELSE 0 + END, + late = 0, + arrived = 0, + remote = 0, + extra_alien = 0, + can_invite = 0 + WHERE raid_id = :raid_id + AND user_id = :user_id + ', + [ + 'raid_id' => $raidId, + 'user_id' => $update['callback_query']['from']['id'] + ]); +// Send vote response. +require_once(LOGIC_PATH . '/update_raid_poll.php'); + +$tg_json = update_raid_poll($raidId, false, $update); + +$alarm_action = ($res['want_invite'] == 0) ? 'want_invite' : $alarm_action = 'no_want_invite'; +$tg_json = alarm($raidId, $update['callback_query']['from']['id'], $alarm_action, '', $tg_json); + +$tg_json[] = answerCallbackQuery($update['callback_query']['id'], getTranslation('vote_updated'), true); + +curl_json_multi_request($tg_json); diff --git a/raidpicture.php b/raidpicture.php index a88953e7..6ae6e764 100644 --- a/raidpicture.php +++ b/raidpicture.php @@ -3,634 +3,75 @@ // Include requirements and perfom initial steps include_once(__DIR__ . '/core/bot/requirements.php'); include_once(CORE_BOT_PATH . '/db.php'); +include_once(LOGIC_PATH . '/raid_picture.php'); if ($metrics){ $requests_total->inc(['raidpicture']); } -// Create GD image object from given URI regardless of file type -function grab_img($uri){ - try { - $img = imagecreatefromstring(file_get_contents($uri)); - } catch (Exception $e) { - info_log($uri, 'Failed to get image:'); - info_log($e->getMessage(), 'Reason: '); - return false; - } - return $img; + +// Define and print picture +// PNG +if($config->RAID_PICTURE_FILE_FORMAT == 'png') { + header("Content-type: image/png"); + +// JPEG +} else if($config->RAID_PICTURE_FILE_FORMAT == 'jpeg' || $config->RAID_PICTURE_FILE_FORMAT == 'jpg') { + header("Content-type: image/jpeg"); + +// Use GIF as default - smallest file size without compression +} else { + header("Content-type: image/gif"); } -if(isset($_GET['sa']) && $_GET['sa'] == 1) $standalone_photo = true; else $standalone_photo = false; -// Debug switch -$debug = false; +$standalone_photo = (isset($_GET['sa']) && $_GET['sa'] == 1) ? true : false; + if(isset($_GET['debug']) && $_GET['debug'] == 1) { - $debug = true; + $raid_id = preg_replace("/\D/","",$_GET['raid']); + if(preg_match("^[0-9]+$^",$raid_id)) $raid = get_raid($raid_id); + else die("Invalid raid id!"); + echo create_raid_picture($raid, $standalone_photo, true); + exit; } + $required_parameters = ['pokemon', 'pokemon_form', 'start_time', 'end_time', 'gym_id', 'ex_raid']; $failed = []; // Raid info foreach($required_parameters as $required) { - if(!array_key_exists($required, $_GET)) { - $failed[] = $required; - } + if(!array_key_exists($required, $_GET)) { + $failed[] = $required; + } } if(count($failed) > 0) { - info_log('Raidpicture called without '.join(', ',$failed).', ending execution'); - exit(); + info_log('Raidpicture called without '.join(', ',$failed).', ending execution'); + exit(); } $raid = []; $raid['pokemon'] = preg_replace("/\D/","",$_GET['pokemon']); $raid['gym_id'] = preg_replace("/\D/","",$_GET['gym_id']); $raid['raid_costume'] = false; +$raid['event'] = ($_GET['ex_raid'] == 1) ? EVENT_ID_EX : 0; if($_GET['start_time'] == 0) { - $raid_ongoing = false; + $raid['raid_ended'] = true; }else { - $raid_ongoing = true; - $raid['start_time'] = date("Y-M-d H:i:s",preg_replace("/\D/","",$_GET['start_time'])); - $raid['end_time'] = date("Y-M-d H:i:s",preg_replace("/\D/","",$_GET['end_time'])); + $raid['raid_ended'] = false; + $raid['start_time'] = date("Y-M-d H:i:s",preg_replace("/\D/","",$_GET['start_time'])); + $raid['end_time'] = date("Y-M-d H:i:s",preg_replace("/\D/","",$_GET['end_time'])); } if(in_array($_GET['pokemon_form'], ['-1','-2','-3'])) { - $raid['pokemon_form'] = $_GET['pokemon_form']; + $raid['pokemon_form'] = $_GET['pokemon_form']; }else { - $raid['pokemon_form'] = preg_replace("/\D/","",$_GET['pokemon_form']); + $raid['pokemon_form'] = preg_replace("/\D/","",$_GET['pokemon_form']); } $raid['costume'] = 0; if(array_key_exists('costume', $_GET) && $_GET['costume'] != '') { - $raid['costume'] = preg_replace("/\D/","",$_GET['costume']); -} -$q_pokemon_info = my_query(" - SELECT - pokemon_form_name, min_cp, max_cp, min_weather_cp, max_weather_cp, weather, shiny, asset_suffix, type, type2 - FROM pokemon - WHERE pokedex_id = '".$raid['pokemon']."' - AND pokemon_form_id = '".$raid['pokemon_form']."' LIMIT 1")->fetch(); -$q_gym_info = my_query("SELECT img_url, gym_name, ex_gym FROM gyms WHERE id='".$raid['gym_id']."'")->fetch(); + $raid['costume'] = preg_replace("/\D/","",$_GET['costume']); +} +$q_pokemon_info = my_query(' + SELECT pokemon_form_name, min_cp, max_cp, min_weather_cp, max_weather_cp, weather, shiny, type, type2 + FROM pokemon + WHERE pokedex_id = ? + AND pokemon_form_id = ? + LIMIT 1', [$raid['pokemon'], $raid['pokemon_form']])->fetch(); +$q_gym_info = my_query('SELECT img_url, gym_name, ex_gym FROM gyms WHERE id = ?', [$raid['gym_id']])->fetch(); $raid = array_merge($raid, $q_pokemon_info, $q_gym_info); -// Fonts -$font_gym = FONTS_PATH . '/' . $config->RAID_PICTURE_FONT_GYM; -$font_text = FONTS_PATH . '/' . $config->RAID_PICTURE_FONT_TEXT; -$font_ex_gym = FONTS_PATH . '/' . $config->RAID_PICTURE_FONT_EX_GYM; - - -// Canvas size -$canvas_width = 700; -$canvas_height = 356; - -// Creating an empty canvas -$canvas = imagecreatetruecolor($canvas_width,$canvas_height); -imagesavealpha($canvas,true); - -// Background color -// Default: White -$bg_rgb = [255,255,255]; -$config_bg_color = explode(',',$config->RAID_PICTURE_BG_COLOR); -if(count($config_bg_color) == 3) { - $bg_rgb = $config_bg_color; -} else { - info_log($config->RAID_PICTURE_BG_COLOR, 'Invalid value RAID_PICTURE_BG_COLOR:'); -} -$bg_color = imagecolorallocate($canvas,$bg_rgb[0],$bg_rgb[1], $bg_rgb[2]); -imagefill($canvas, 0, 0, $bg_color); - -// Text / Font color -// Default: Black -$font_rgb = [0,0,0]; -$config_font_color = explode(',',$config->RAID_PICTURE_TEXT_COLOR); -if(count($config_font_color) == 3) { - $font_rgb = $config_font_color; -} else { - info_log($config->RAID_PICTURE_TEXT_COLOR, 'Invalid value RAID_PICTURE_TEXT_COLOR:'); -} -$font_color = imagecolorallocate($canvas,$font_rgb[0],$font_rgb[1],$font_rgb[2]); - -// Defining RBG values that are used to create transparent color -// Should be different from RAID_PICTURE_BG_COLOR and RAID_PICTURE_TEXT_COLOR -$transparent_rgb = [0,255,0]; - -// Gym image -$gym_url = $raid['img_url']; -if($config->RAID_PICTURE_STORE_GYM_IMAGES_LOCALLY && !empty($gym_url)) { - if(substr($gym_url, 0, 7) == 'file://') { - $gym_image_path = $gym_url; - debug_log($gym_image_path, 'Found an image imported via a portal bot: '); - }else { - $file_name = explode('/', $gym_url)[3]; - $gym_image_path = PORTAL_IMAGES_PATH .'/'. $file_name.'.png'; - debug_log($gym_image_path, 'Attempting to use locally stored gym image'); - if(!file_exists($gym_image_path)) { - debug_log($gym_url, 'Gym image not found, attempting to downloading it from: '); - if(is_writable(PORTAL_IMAGES_PATH)) { - download_Portal_Image($gym_url, PORTAL_IMAGES_PATH, $file_name . '.png'); - }else { - $gym_image_path = $gym_url; - info_log(PORTAL_IMAGES_PATH, 'Failed to write new gym image, incorrect permissions in directory '); - } - } - } -}else { - $img_gym = false; - if (!empty($gym_url)) { - $gym_image_path = $gym_url; - } -} -$img_gym = grab_img($gym_image_path); -if($img_gym == false) { - info_log($gym_image_path, 'Loading the gym image failed, using default gym image'); - if(is_file($config->RAID_DEFAULT_PICTURE)) { - $img_gym = grab_img($config->RAID_DEFAULT_PICTURE); - } else { - info_log($config->RAID_DEFAULT_PICTURE, 'Cannot read default gym image:'); - $img_gym = grab_img(IMAGES_PATH . "/gym_default.png"); - } -} - -// Get the width and height of the gym picture -$gym_w = imagesx($img_gym); -$gym_h = imagesy($img_gym); - -// Crop gym image -if($gym_w > $gym_h) { - $size = $gym_h; - $crop_x = floor((($gym_w/2)-($gym_h/2))); - $crop_y = 0; -} else { - $size = $gym_w; - $crop_x = 0; - $crop_y = floor((($gym_h/2)-($gym_w/2))); -} - -// Create mask -$new_w = 300; -$new_h = 300; -$mask = imagecreatetruecolor($new_w,$new_h); - -// Fill the mask with background color -$bg = imagecolorallocate($mask,$bg_rgb[0],$bg_rgb[1], $bg_rgb[1]); -imagefill($mask,0,0,$bg); - -// Define transparent color for the mask -$transparent = imagecolorallocate($mask,$transparent_rgb[0],$transparent_rgb[1],$transparent_rgb[2]); -imagecolortransparent($mask,$transparent); - -// Creating the orange circle around the gym photo -$color_ellipse = imagecolorallocate($mask,254,193,161); -imagefilledellipse($mask,$new_w/2,$new_h/2,$new_w-9,$new_h-9,$color_ellipse); -imagefilledellipse($mask,$new_w/2,$new_h/2,$new_w-16,$new_h-16,$bg); - -// Creating a circle that is filled with transparent color -imagefilledellipse($mask,$new_w/2,$new_h/2,$new_w-30,$new_h-30,$transparent); - -// Merging the desired part of the gym picture with canvas -imagecopyresampled($canvas,$img_gym,0,0,$crop_x,$crop_y,$new_w,$new_h, $size,$size); - -// Merging the mask with a circular cutout to the canvas -imagecopymerge($canvas, $mask, 0, 0, 0, 0, $new_w, $new_h, 100); - - - -// Is ex gym? -if($raid['ex_gym'] == 1) { - $ex_text_size = 20; - $ex_text_angle = 0; - $corner = 16; // Roundness of the corners - $extra = $ex_text_size/5+1; // Some extra height - - $ex_mark_bg_color = [94,169,190]; - $ex_mark_text_color = [255,255,255]; - - // Get the text with local translation for EX Raid gym - $ex_raid_gym_text = strtoupper(getPublicTranslation('ex_gym')); - // Finding out the size of text - $ex_text_box = imagettfbbox($ex_text_size,$ex_text_angle,$font_ex_gym,$ex_raid_gym_text); - - $ex_logo_width = $ex_text_box[2]+($corner); - $ex_logo_height = $ex_text_size+$extra; - - - // Create the canvas for EX RAID indicator - $ex_logo = imagecreatetruecolor($ex_logo_width,$ex_logo_height); - // Defining the transparent color - $ex_transparent = imagecolorallocate($ex_logo,$transparent_rgb[0],$transparent_rgb[1],$transparent_rgb[2]); - imagecolortransparent($ex_logo,$ex_transparent); - // Defining background color - $ex_logo_bg = imagecolorallocate($mask,$ex_mark_bg_color[0],$ex_mark_bg_color[1], $ex_mark_bg_color[2]); - $ex_text_color = imagecolorallocate($ex_logo,$ex_mark_text_color[0],$ex_mark_text_color[1],$ex_mark_text_color[2]); - - //Filling the canvas with transparent color - imagefill($ex_logo,0,0,$ex_transparent); - - // Creating 4 balls, one in each corner - imagefilledellipse($ex_logo,$corner/2,$corner/2,$corner,$corner,$ex_logo_bg); - imagefilledellipse($ex_logo,$corner/2,$ex_logo_height-$corner/2,$corner,$corner,$ex_logo_bg); - imagefilledellipse($ex_logo,$ex_logo_width-$corner/2,$corner/2,$corner,$corner,$ex_logo_bg); - imagefilledellipse($ex_logo,$ex_logo_width-$corner/2,$ex_logo_height-$corner/2,$corner,$corner,$ex_logo_bg); - // And two rectangles to fill the rest - imagefilledrectangle($ex_logo,$corner/2,0,$ex_logo_width-($corner/2),$ex_logo_height,$ex_logo_bg); - imagefilledrectangle($ex_logo,0,$corner/2,$ex_logo_width,$ex_logo_height-($corner/2),$ex_logo_bg); - - // Draw the text - imagettftext($ex_logo,$ex_text_size,$ex_text_angle,$corner/2,$ex_text_size+1,$ex_text_color,$font_ex_gym,$ex_raid_gym_text); - - // Copy icon into canvas - imagecopy($canvas,$ex_logo,20,20,0,0,$ex_logo_width,$ex_logo_height); -} - - -$show_boss_pokemon_types = false; -// Raid running -if($raid_ongoing) { - if(strlen($raid['asset_suffix']) > 2) { - $icon_suffix = $raid['asset_suffix']; - }else { - $pad_zeroes = ''; - for ($o=3-strlen($raid['pokemon']);$o>0;$o--) { - $pad_zeroes .= 0; - } - $icon_suffix = $pad_zeroes.$raid['pokemon'] . "_" . $raid['asset_suffix']; - } - - // Raid Egg - if($raid['pokemon'] > 9990) { - // Getting the actual icon - $img_pokemon = grab_img(IMAGES_PATH . "/raid_eggs/pokemon_icon_" . $raid['pokemon'] . "_00.png"); - - // Position and size of the picture - $dst_x = $dst_y = 150; - $dst_w = $dst_h = 200; - $src_w = $src_h = 128; - - //Pokemon - } else { - // Formatting the id from 1 digit to 3 digit (1 -> 001) - $pokemon_id = str_pad($raid['pokemon'], 3, '0', STR_PAD_LEFT); - - // Check pokemon icon source and create image - $img_file = null; - $p_sources = explode(',', $config->RAID_PICTURE_POKEMON_ICONS); - - $addressable_icon = 'pm'.$raid['pokemon']; - if($raid['pokemon_form_name'] != 'normal') $addressable_icon .= '.f'.strtoupper($raid['pokemon_form_name']); - - // Getting the actual icon filename - $p_icon = "pokemon_icon_" . $icon_suffix; - if($raid['costume'] != 0) { - $p_icon .= '_' . str_pad($raid['costume'], 2, '0', STR_PAD_LEFT); - - $costume = json_decode(file_get_contents(ROOT_PATH . '/protos/costume.json'), true); - $addressable_icon .= '.c' . array_search($raid['costume'],$costume); - } - if($raid['shiny'] == 1 && $config->RAID_PICTURE_SHOW_SHINY) { - $p_icon = $p_icon . "_shiny"; - $addressable_icon .= '.s'; - $shiny_icon = grab_img(IMAGES_PATH . "/shinystars.png"); - } - $addressable_icon .= '.icon.png'; - $p_icon = $p_icon . ".png"; - - foreach($p_sources as $p_dir) { - // Set pokemon icon dir - $p_img = IMAGES_PATH . "/pokemon_" . $p_dir . "/" . $p_icon; - $p_add_img = IMAGES_PATH . "/pokemon_" . $p_dir . "/Addressable_Assets/" . $addressable_icon; - - // Icon dir named 'pokemon'? Then change path to not add '_repo-owner' to icon folder name - if($p_dir == 'pokemon') { - $p_img = IMAGES_PATH . "/pokemon/" . $p_icon; - $p_add_img = IMAGES_PATH . "/pokemon/Addressable_Assets" . $addressable_icon; - } - // Check if file exists in this collection - // Prioritize addressable asset file - if(file_exists($p_add_img) && filesize($p_add_img) > 0) { - $img_file = $p_add_img; - break; - }else if(file_exists($p_img) && filesize($p_img) > 0) { - $img_file = $p_img; - break; - } - } - - // If no image was found, substitute with a fallback - if($img_file === null) { - info_log($p_icon, 'Failed to find an image in any pokemon image collection for:'); - $img_fallback_file = null; - // If we know the raid level, fallback to egg image - if(array_key_exists('level', $raid) && $raid['level'] !== null && $raid['level'] != 0) { - $img_fallback_file = IMAGES_PATH . "/raid_eggs/pokemon_icon_999" . $raid['level'] . "_00.png"; - } else { - info_log('Unknown raid level, using fallback icon.'); - $img_fallback_file = $config->RAID_PICTURE_POKEMON_FALLBACK; - } - $img_file = $img_fallback_file; - } - - $img_pokemon = grab_img($img_file); - - // Position and size of the picture - $dst_x = $dst_y = 100; - $dst_w = $dst_h = $src_w = $src_h = 256; - - if($raid['type'] != '') $show_boss_pokemon_types = true; - } - -// Raid ended -} else { - // Raid won image - $img_pokemon = grab_img(IMAGES_PATH . "/raidwon.png"); - - // Position and size of the picture - $dst_x = $dst_y = 172; - $src_w = 444; - $src_h = 512; - $dst_w = 160; - $dst_h = floor($dst_w/$src_w*$src_h); -} - -// Create pokemon image. -imagesavealpha($img_pokemon,true); - -// Debug - Add border around pokemon image -if($debug) { - $im = imagecreate($src_w,$src_h); - $black = imagecolorallocate($im,0,0,0); - imagerectangle($img_pokemon,0,0,$src_w-1,$src_h-1,$black); -} - -// Add pokemon to image -imagecopyresampled($canvas,$img_pokemon,$dst_x,$dst_y,0,0,$dst_w,$dst_h,$src_w,$src_h); - -// Add pokemon types -if($config->RAID_PICTURE_POKEMON_TYPES && $show_boss_pokemon_types) { - $img_type = grab_img(IMAGES_PATH . "/types/".$raid['type'].".png"); - $type1_x = 300; - imagesavealpha($img_type, true); - if($raid['type2'] != '') { - $img_type2 = grab_img(IMAGES_PATH . "/types/".$raid['type2'].".png"); - imagesavealpha($img_type2, true); - imagecopyresampled($canvas,$img_type2,300,300,0,0,40,40,64,64); - $type1_x -= 50; - } - imagecopyresampled($canvas,$img_type,$type1_x,300,0,0,40,40,64,64); -} -if(isset($shiny_icon)) { - imagesavealpha($shiny_icon,true); - $light_white = imagecolorallocatealpha($canvas, 255,255,255,50); - imagefilledellipse($canvas, $type1_x-35 ,320,40,40,$light_white); - imagecopyresampled($canvas,$shiny_icon,$type1_x-52,301,0,0,35,35,100,100); -} - -// Ex-Raid? -if($_GET['ex_raid'] == '1') { - $img_expass = grab_img(IMAGES_PATH . "/expass.png"); - imagesavealpha($img_expass,true); - - // Debug - Add border around expass image - if($debug) { - $im = imagecreate(256,256); - $black = imagecolorallocate($im,0,0,0); - imagerectangle($img_expass,0,0,255,255,$black); - } - imagecopyresampled($canvas,$img_expass,0,225,0,0,100,100,256,256); -} - - - -// Adding the gym name to the image -$text_size = 23; // Font size of additional text -$text_size_cp_weather = 20;// Font size of weather cp text -$left_after_poke = 356; // First left position behind the pokemon icon. -$angle = 0; // Angle of the text -$spacing = 10; // Spacing between lines -$spacing_right = 10; // Empty space on the right for weather icons and CP text - - - -// Gym name -// Largest gym name we found so far for testing: -//$gym_name = 'Zentrum für Junge Erwachsene der Kirche Jesu Christi der Heiligen der Letzten Tage Pfahl Düsseldorf'; -$gym_name = $raid['gym_name']; - -// Get length, the shortest and largest word of the gym name -$gym_name_words = explode(SP, $gym_name); -$gym_name_word_lengths = array_map('strlen', array_map('utf8_decode', $gym_name_words)); -$gym_name_word_largest = max($gym_name_word_lengths); -$gym_name_word_shortest = min($gym_name_word_lengths); -$gym_name_total_chars = strlen(utf8_decode($gym_name)); - -// Number of rows based on number of words or total chars -$gym_name_rows = 1; -if(count($gym_name_words) > 1 && $gym_name_total_chars >= 18 && $gym_name_total_chars <= 50) { - $gym_name_rows = 2; -} else if($gym_name_total_chars > 50) { - $gym_name_rows = 3; -} - -// Wrap gym name to multiple lines if too long -$gym_name_lines = explode(PHP_EOL,wordwrap(trim($gym_name),floor(($gym_name_total_chars+$gym_name_word_largest)/$gym_name_rows),PHP_EOL)); - -debug_log($gym_name_total_chars, 'Gym name length:'); -debug_log($gym_name_lines, 'Gym name lines:'); - -// Target width and height -$targetWidth = imagesx($canvas) - imagesx($mask) - $spacing_right; -$targetHeight = 95; -$targetHeight = $targetHeight/$gym_name_rows; - -// Get largest possible fontsize for each gym name line -for($l=0; $l= $targetWidth || $height >= $targetHeight){ - break; - } - } - - // Gym name font size and spacing - if($l == 0 || $targetsize < $fontsize_gym) { - $fontsize_gym = $targetsize; - $spacing_gym = $height * 0.30; - } -} - -// Add gym name to image -for($y=0;$y 20) { - $pokemon_text_lines = explode(SP,$pokemon_name); - if(count($pokemon_text_lines) == 1) { - // Wrapping the time text if too long (to 20 letters) - $pokemon_text_lines = explode(PHP_EOL,wordwrap(trim($pokemon_name),20,PHP_EOL)); - } -} -$num_pokemon_lines = count($pokemon_text_lines); - -// Target width and height -$targetWidth = imagesx($canvas) - $left_after_poke - (strlen($raid['weather']) * 42) - $spacing_right; -$targetHeight = 80; - -// Get largest possible fontsize for each pokemon name and form line -for($p=0; $p<($num_pokemon_lines); $p++) { - for($s=1; $s<40; $s=$s+0.5){ - $box = imagettfbbox($s, 0, $font_text, $pokemon_text_lines[$p]); - $min_x = min(array($box[0], $box[2], $box[4], $box[6])); - $max_x = max(array($box[0], $box[2], $box[4], $box[6])); - $min_y = min(array($box[1], $box[3], $box[5], $box[7])); - $max_y = max(array($box[1], $box[3], $box[5], $box[7])); - $width = ($max_x - $min_x); - $height = ($max_y - $min_y); - $targetsize = $s; - // Exit once we exceed width or height - if($width >= $targetWidth || $height >= $targetHeight){ - break; - } - } - - // Gym name font size and spacing - if($p == 0 || $targetsize < $fontsize_poke) { - $fontsize_poke = $targetsize; - } -} - -// Pokemon name (and form) in 1 row -$poke_text_top = 310; - -// Pokemon name and form in one or two lines? -if($num_pokemon_lines > 1) { - $poke_text_top = 272; -} - -// If the photo is sent without caption, we want to keep the bottom right corcer clear of text because Telegram covers it with a timestamp -if($standalone_photo) $poke_text_top -= 50; - -// Add pokemon name to image -for($pa=0;$pa<$num_pokemon_lines;$pa++){ - // Get text width and height - $textwidth = ($max_x - $min_x); - $textheight = ($max_y - $min_y); - // Position from top - $poke_text_top = ($poke_text_top+($pa*($fontsize_poke+$spacing))); - imagettftext($canvas,$fontsize_poke,$angle,$left_after_poke,$poke_text_top,$font_color,$font_text,$pokemon_text_lines[$pa]); -} - -// Pokemon CP -if($raid['pokemon'] < 9990) { - $cp_text_top = $poke_text_top+$text_size+$spacing; - $cp_text = $raid['min_cp']." - ".$raid['max_cp']; - $cp_text2 = "(".$raid['min_weather_cp']."-".$raid['max_weather_cp'].")"; - - imagettftext($canvas,$text_size,$angle,$left_after_poke,$cp_text_top,$font_color,$font_text,$cp_text); - $cp_weather_text_box = imagettfbbox($text_size_cp_weather,$angle,$font_text,$cp_text2); - imagettftext($canvas,$text_size_cp_weather,$angle,($canvas_width-$cp_weather_text_box[2]-$spacing_right),$cp_text_top,$font_color,$font_text,$cp_text2); - - $count_weather = strlen($raid['weather']); - for($i=0;$i<$count_weather;$i++) { - $we = substr($raid['weather'],$i,1); - $weather_icon_path = IMAGES_PATH . "/weather/"; - // Use white icons? - if($config->RAID_PICTURE_ICONS_WHITE) { - $weather_icon_path = IMAGES_PATH . "/weather_white/"; - } - $weather_icon = grab_img($weather_icon_path . $we . ".png"); // 64x64 - imagecopyresampled($canvas,$weather_icon,$canvas_width-$spacing_right-($count_weather-$i)*40,$poke_text_top-30,0,0,38,38,64,64); - } -} - - - -// Define and print picture -// PNG -if($config->RAID_PICTURE_FILE_FORMAT == 'png') { - header("Content-type: image/png"); - imagepng($canvas); - -// JPEG -} else if($config->RAID_PICTURE_FILE_FORMAT == 'jpeg' || $config->RAID_PICTURE_FILE_FORMAT == 'jpg') { - header("Content-type: image/jpeg"); - imagejpeg($canvas, NULL, 90); - -// Use GIF as default - smallest file size without compression -} else { - header("Content-type: image/gif"); - imagegif($canvas); -} - -?> +echo create_raid_picture($raid, $standalone_photo); diff --git a/sql/pokemon-raid-bot.sql b/sql/0-pokemon-raid-bot.sql similarity index 84% rename from sql/pokemon-raid-bot.sql rename to sql/0-pokemon-raid-bot.sql index 6d3ec13f..9dd0601d 100644 --- a/sql/pokemon-raid-bot.sql +++ b/sql/0-pokemon-raid-bot.sql @@ -23,9 +23,12 @@ CREATE TABLE `cleanup` ( `raid_id` int(10) unsigned NOT NULL, `chat_id` bigint(20) NOT NULL, `message_id` bigint(20) unsigned NOT NULL, + `thread_id` int(10) UNSIGNED NULL, `type` VARCHAR(20) NULL, `date_of_posting` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`id`) + `media_unique_id` varchar(45) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `unique_chat_msg` (`chat_id`,`message_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `events` ( `id` int(3) unsigned NOT NULL AUTO_INCREMENT, @@ -35,6 +38,7 @@ CREATE TABLE `events` ( `time_slots` int(3) DEFAULT NULL, `raid_duration` int(3) unsigned NOT NULL DEFAULT 0, `hide_raid_picture` tinyint(1) DEFAULT 0, + `pokemon_title` tinyint(1) NULL DEFAULT 1, `poll_template` VARCHAR(200) NULL DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; @@ -58,15 +62,29 @@ CREATE TABLE `overview` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `chat_id` bigint(20) NOT NULL, `message_id` bigint(20) unsigned NOT NULL, + `thread_id` int(10) UNSIGNED NULL, `chat_title` varchar(128) DEFAULT NULL, `chat_username` varchar(32) DEFAULT NULL, `updated` date DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE TABLE `photo_cache` ( + `id` varchar(100) NOT NULL, + `unique_id` varchar(45) NOT NULL, + `pokedex_id` int(10) DEFAULT NULL, + `form_id` int(4) DEFAULT NULL, + `raid_id` int(10) unsigned DEFAULT NULL, + `ended` tinyint(1) DEFAULT NULL, + `start_time` datetime DEFAULT NULL, + `end_time` datetime DEFAULT NULL, + `gym_id` int(10) unsigned DEFAULT NULL, + `standalone` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`unique_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `pokemon` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `pokedex_id` int(10) unsigned NOT NULL, - `pokemon_name` varchar(12) DEFAULT NULL, + `pokemon_name` varchar(30) DEFAULT NULL, `pokemon_form_name` varchar(45) DEFAULT NULL, `pokemon_form_id` int(4) DEFAULT NULL, `min_cp` int(10) unsigned NOT NULL DEFAULT 0, @@ -77,7 +95,6 @@ CREATE TABLE `pokemon` ( `type` varchar(10) DEFAULT '', `type2` varchar(10) DEFAULT '', `shiny` tinyint(1) unsigned DEFAULT 0, - `asset_suffix` varchar(45) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `idx_pokemon_pokedex_id_pokemon_form_id` (`pokedex_id`,`pokemon_form_id`) ) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8mb4; @@ -87,8 +104,9 @@ CREATE TABLE `raid_bosses` ( `pokemon_form_id` int(4) DEFAULT NULL, `date_start` datetime NOT NULL DEFAULT '1970-01-01 00:00:01', `date_end` datetime NOT NULL DEFAULT '2038-01-19 03:14:07', - `raid_level` enum('1','2','3','4','5','6','X') DEFAULT NULL, + `raid_level` TINYINT UNSIGNED DEFAULT NULL, `scheduled` TINYINT(1) NULL DEFAULT 0, + `disabled` TINYINT(1) UNSIGNED DEFAULT 0, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `raids` ( @@ -101,7 +119,7 @@ CREATE TABLE `raids` ( `end_time` datetime DEFAULT NULL, `gym_team` enum('mystic','valor','instinct') DEFAULT NULL, `gym_id` int(10) unsigned NOT NULL, - `level` enum('1','2','3','4','5','6','X') DEFAULT NULL, + `level` TINYINT UNSIGNED DEFAULT NULL, `move1` varchar(255) DEFAULT NULL, `move2` varchar(255) DEFAULT NULL, `gender` varchar(255) DEFAULT NULL, @@ -116,11 +134,12 @@ CREATE TABLE `trainerinfo` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `chat_id` bigint(20) NOT NULL, `message_id` bigint(20) unsigned NOT NULL, + `thread_id` int(10) UNSIGNED NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `user_input` ( `id` int(11) NOT NULL AUTO_INCREMENT, - `user_id` int(11) DEFAULT NULL, + `user_id` bigint(20) DEFAULT NULL, `handler` varchar(45) DEFAULT NULL, `modifiers` text DEFAULT NULL, PRIMARY KEY (`id`) @@ -139,6 +158,8 @@ CREATE TABLE `users` ( `lang_manual` TINYINT UNSIGNED NOT NULL DEFAULT 0, `tutorial` TINYINT(1) NOT NULL DEFAULT 0, `auto_alarm` TINYINT(1) UNSIGNED NULL DEFAULT 0, + `gymarea` TINYINT UNSIGNED NULL, + `privileges` TEXT NULL, PRIMARY KEY (`id`), UNIQUE KEY `i_userid` (`user_id`) ) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8mb4; diff --git a/sql/upgrade/3.sql b/sql/upgrade/3.sql index b3a3b6b8..755e1a54 100644 --- a/sql/upgrade/3.sql +++ b/sql/upgrade/3.sql @@ -4,15 +4,14 @@ CREATE UNIQUE INDEX IF NOT EXISTS `idx_pokemon_pokedex_id_pokemon_form_id` ON `p ALTER TABLE `raids` DROP COLUMN IF EXISTS `first_seen`, -ADD COLUMN IF NOT EXISTS `spawn` DATETIME NULL DEFAULT NULL AFTER `pokemon_form`, MODIFY `pokemon` int(4) DEFAULT NULL, ADD COLUMN IF NOT EXISTS `pokemon_form` int(4) unsigned NOT NULL DEFAULT 0, +ADD COLUMN IF NOT EXISTS `spawn` DATETIME NULL DEFAULT NULL AFTER `pokemon_form`, ADD COLUMN IF NOT EXISTS `level` enum('1','2','3','4','5','6','X') DEFAULT NULL AFTER `gym_id`, ADD COLUMN IF NOT EXISTS `event` int(3) unsigned DEFAULT NULL AFTER `gender`, ADD COLUMN IF NOT EXISTS `event_note` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL AFTER `event`; CREATE TABLE IF NOT EXISTS `raid_bosses` (`id` int(11) NOT NULL AUTO_INCREMENT,`pokedex_id` int(10) DEFAULT NULL,`pokemon_form_id` int(4) DEFAULT NULL,`date_start` datetime NOT NULL DEFAULT '1970-01-01 00:00:01',`date_end` datetime NOT NULL DEFAULT '2038-01-19 03:14:07',`raid_level` enum('1','2','3','4','5','6','X') DEFAULT NULL,PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -INSERT INTO `raid_bosses` (`pokedex_id`,`pokemon_form_id`,`raid_level`) SELECT `pokedex_id`,`pokemon_form_id`,`raid_level` FROM pokemon WHERE raid_level != '0'; ALTER TABLE `users` ADD COLUMN IF NOT EXISTS `trainername` varchar(200) CHARACTER SET utf8mb4 DEFAULT NULL AFTER `name`, diff --git a/sql/upgrade/5.sql b/sql/upgrade/5.sql new file mode 100644 index 00000000..ef26c0fb --- /dev/null +++ b/sql/upgrade/5.sql @@ -0,0 +1,23 @@ +ALTER TABLE user_input CHANGE COLUMN IF EXISTS `user_id` `user_id` BIGINT(20) DEFAULT NULL AFTER `id`; +ALTER TABLE `users` ADD COLUMN IF NOT EXISTS `gymarea` TINYINT UNSIGNED NULL AFTER `auto_alarm`; +ALTER TABLE `users` ADD COLUMN IF NOT EXISTS `privileges` TEXT NULL AFTER `gymarea`; + +CREATE TABLE IF NOT EXISTS `photo_cache` (`id` varchar(100) NOT NULL, `unique_id` varchar(45) NOT NULL, `pokedex_id` int(10) DEFAULT NULL, `form_id` int(4) DEFAULT NULL, `raid_id` int(10) unsigned DEFAULT NULL, `ended` tinyint(1) DEFAULT NULL, `end_time` DATETIME NULL, `start_time` DATETIME NULL, `gym_id` int(10) unsigned DEFAULT NULL, `standalone` tinyint(1) NOT NULL DEFAULT '0', PRIMARY KEY (`unique_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +ALTER TABLE `photo_cache` ADD COLUMN IF NOT EXISTS `end_time` DATETIME DEFAULT NULL AFTER `ended`; +ALTER TABLE `photo_cache` ADD COLUMN IF NOT EXISTS `start_time` DATETIME DEFAULT NULL AFTER `end_time`; + +ALTER TABLE `cleanup` ADD COLUMN IF NOT EXISTS `media_unique_id` varchar(45) DEFAULT NULL AFTER `date_of_posting`; +ALTER TABLE `cleanup` ADD COLUMN IF NOT EXISTS `thread_id` INT UNSIGNED NULL AFTER `message_id`; +CREATE UNIQUE INDEX IF NOT EXISTS `unique_chat_msg` ON `cleanup` (chat_id, message_id); + +ALTER TABLE `overview` ADD COLUMN IF NOT EXISTS `thread_id` INT UNSIGNED NULL AFTER `message_id`; + +ALTER TABLE `trainerinfo` ADD COLUMN IF NOT EXISTS `thread_id` INT UNSIGNED NULL AFTER `message_id`; + +ALTER TABLE `raids` CHANGE COLUMN IF EXISTS `level` `level` TINYINT UNSIGNED DEFAULT NULL; +ALTER TABLE `raid_bosses` CHANGE COLUMN IF EXISTS `raid_level` `raid_level` TINYINT UNSIGNED DEFAULT NULL; + +ALTER TABLE `events` ADD COLUMN IF NOT EXISTS `pokemon_title` TINYINT(1) NULL DEFAULT 1 AFTER `hide_raid_picture`; + +ALTER TABLE `pokemon` DROP COLUMN IF EXISTS `asset_suffix`; +ALTER TABLE `pokemon` CHANGE COLUMN IF EXISTS `pokemon_name` `pokemon_name` varchar(30) DEFAULT NULL; diff --git a/sql/upgrade/6.sql b/sql/upgrade/6.sql new file mode 100644 index 00000000..ef70b947 --- /dev/null +++ b/sql/upgrade/6.sql @@ -0,0 +1 @@ +ALTER TABLE `raid_bosses` ADD COLUMN IF NOT EXISTS `disabled` TINYINT(1) UNSIGNED DEFAULT 0 AFTER `scheduled`;