├── .eslintrc.js ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── pull_request_template.md ├── .gitignore ├── LICENSE ├── LootBot ├── README_ISNEW.md ├── logic │ ├── craft.js │ ├── inventory.js │ ├── items.js │ ├── master_craftsman.js │ ├── necro_descent │ │ ├── nd_combact.js │ │ ├── nd_logic.js │ │ ├── nd_maze.js │ │ └── nd_player.js │ └── players.js ├── message_managers │ ├── reply_to_manager.js │ ├── specific │ │ ├── master_craftsman.js │ │ └── necro_descent.js │ └── views_util.js ├── model │ ├── DB_managers │ │ ├── db.js │ │ ├── queries.js │ │ └── specific │ │ │ ├── db_config.js │ │ │ ├── inventory.js │ │ │ ├── items.js │ │ │ └── players.js │ └── JSON_managers │ │ ├── json_model.js │ │ └── specifiche │ │ ├── master_craftsman.js │ │ └── necro_descent.js ├── sources │ └── necro_descent │ │ └── idea.rtf ├── utility │ ├── bot_response.js │ ├── config_sample.js │ ├── specifics │ │ ├── master_craftsman.js │ │ └── necro_descent.js │ └── utils.js └── views │ ├── errors.js │ ├── specific │ ├── master_craftsman.js │ └── necro_descent.js │ └── strings.js ├── LootBot_Struct.sql ├── README.md ├── config.js_default ├── events.ini_default ├── lb_problem.js ├── lbp_problem.js ├── loot-nginx-conf ├── lootBot.js ├── lootBotPlus.js ├── mobGenerator.js ├── package-lock.json ├── package.json ├── pdfkit-tables.js └── suggestions ├── models ├── SuggestionsTables │ └── allTables_struct.json ├── bandit_w.txt └── tips_message_model.js ├── tips_message_controller.js └── tips_utils.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | commonjs: true, 4 | es2021: true, 5 | node: true 6 | }, 7 | extends: [ 8 | 'standard' 9 | ], 10 | parserOptions: { 11 | ecmaVersion: 12 12 | }, 13 | rules: { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Segnalazione bug 3 | about: Template per la segnalazione di bug 4 | title: '' 5 | labels: 'bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Descrizione del bug 11 | 12 | 13 | 14 | ## Come riprodurlo 15 | 16 | Passi per riprodurre il problema: 17 | 1. Vai a '...' 18 | 2. Clicca su '....' 19 | 3. Scrivi '....' 20 | 4. Invia il messaggio 21 | 22 | ## Comportamento previsto 23 | 24 | 25 | 26 | ## Screenshot 27 | 28 | 29 | 30 | ## Messaggi precedenti di Telegram 31 | 32 | 33 | 34 | ## Indicazioni aggiuntive 35 | 36 | 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/pull_request_template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Proposta funzionalità o fix 3 | about: Template per la proposta di funzionalità 4 | title: '' 5 | labels: 'feature' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Descrizione 11 | 12 | 13 | 14 | ## Issue collegate 15 | 16 | 17 | 18 | ## Motivazione e contesto 19 | 20 | 21 | 22 | ## Hai testato il codice? 23 | 24 | 25 | 26 | ## Screenshot 27 | 28 | 29 | 30 | ## Indicazioni aggiuntive 31 | 32 | 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | config.js 2 | events.ini 3 | node_modules/ 4 | node_modules_old/ 5 | custom_log.log 6 | LootBot/sources/players/ -------------------------------------------------------------------------------- /LootBot/README_ISNEW.md: -------------------------------------------------------------------------------- 1 | 3.06.2023, nrc 2 | 3 | 4 | # Loot Game Bot - Una proposta di struttura modulare 5 | _Questa è una proposta, e riconsosco che abbia dei limiti e sia ancora in una fase embrionale. 6 | Probabilmente ancora non è adatta ad ospitare la logica di lootBot.js in maniera salabile ed ordinata, ma credo possa essere un buon punto di partenza._ 7 | 8 | Seguono dei "paradigmi" 9 | 10 | ## ./model/ 11 | Nel model va solo e solamente la comunicazione con la persistenza (al momento database e file json(novità) ) 12 | 13 | .**DB_managers** 14 | - Il modulo db.js si occupa di creare la connessione ed esporre il metodo di query. Non dovrebbe far altro 15 | - il modulo queries.js ospita le stringhe delle query (come funzioni). <- Può decisamente essere migliorato! 16 | - `./specifics` ospita le `db_config` (che eredita da config ed espone le strutture delle tabelle) e tutti i moduli dei modles specifici 17 | - modles specifici: sono quelli che andranno pian piano aggiornati (e ne serviranno di nuovi). È importante specificare che questi moduli si limiteranno a dichiarare delle funzioni, eseguire le query specifiche e restituire i risultati in alto. **STOP!** 18 | 19 | .**JSON_managers** 20 | - Il concetto è lo stesso che per il database. La pratica piu semplice. 21 | json_model contiene le funzione di verifica file, lettura ed aggiornamento. 22 | Nei modelli specifici si dichiarano i percorsi e le funzioni specifiche. 23 | 24 | **Nota sul return dei modelli specifici** 25 | È bene che a questo livello le funzioni seguano una logica uniforme: 26 | I return devono essere del tipo object: 27 | `{(boolean)esit, (string)message_text ? || result ?}` 28 | 29 | - `esit` verrà verificato dal livello superiore (logic) 30 | - `message_text` è opzionale, conterrà eventuali messaggi (tipicamente d'errore) 31 | - `result` è opzionale, _può contenere oggetti, array... dipende dalla logica piu in alto_ 32 | 33 | 34 | 35 | ## ./logics/ 36 | I moduli in questa sottocartella si occupano di definire funzioni piu o meno complesse, ma limitandosi ad usare il proprio model specifico o interagendo con altri moduli in logics. 37 | Se si occupano di messaggi, come per i models, si occupano di messaggi di errore o altrimenti fanno riferimento alla propria view (`../views/specifics`). 38 | le funzioni che espongono, se asincrone, devono rispettare la logica del ritorno dei models (`{(boolean)esit, (string)message_text ? || result ?}`) 39 | 40 | 41 | 42 | ## ./message_managers/ 43 | Quelli in `/specifics` sono il middelware tra lootBot.js (che idealmente si occuperà solo di smistare i messaggi e le query ricevute verso i message_managers) e ./utility/bot_response. 44 | Possono accedere alle logiche specifiche (dove possono avere una diretta controparte) e alla loro vista specifica 45 | Si occupano quindi di formare messaggi e query, utilizzando le funzioni offerte dalle logics. 46 | 47 | In views_util sono definite costanti di accesso comune, come il menu dei bottoni 48 | 49 | **Nota sul return dei modelli specifici:** 50 | - È fondamentale che a questo livello le funzioni seguano una logica uniforme: 51 | - i return devono essere del tipo `{query ?, toSend ?, toEdit ?, toDelete ?, sendObject ?, sendFile ?}` o un array di questo oggetto. 52 | 53 | 54 | 55 | ## ./sources/ 56 | È la directory pronta ad ospitare la persistenza in json di model specifici. 57 | Per il momento comprende `./players` al cui interno ogni giocatore ha una cartella dedicata. _-> sarebbe il posto dove spostare pian piano parte della struttura della tabella player, ad esempio_ 58 | 59 | 60 | 61 | ## ./utility/ 62 | Oltre a `config` e `utils` (il file configurazione e un modulo di utilità generiche) qui è ospitato `bot_response`. 63 | - `bot_response` è un modulo in grado di occuparsi della risposta a query, dell'invio di file e della modifica o invio di messaggi (con controllo sulla lunghezza del testo ed eventuale divisione in piu messaggi). 64 | - `bot_response` espone anche dei costruttori per gli oggetti di response `(query, toSend, toEdit, toDelete, sendObject, sendFile` 65 | 66 | in `specifics` sono definite delle strutture specifiche accessorie ad un `message_manager`, come ad esempio l'albero query 67 | 68 | 69 | 70 | ## ./views/ 71 | in `strings.js` possono essere definite le funzioni di manipolazione stringhe (come l'inserimento o la lettura di variabili). 72 | Un modulo generico errors.js raccoglie le stringhe di errore generalmente usate a livello di logics e models (dunque non serve che ognuno ne abbia di specifici). 73 | In `/specifics` invece sono raccolti i moduli con le stringhe usate per formulare messaggi nei message_managers. 74 | È bene notare che (ad esclusione che le stringhe verso l'utente devono tutte essere raggruppate qui). 75 | 76 | _Al momento non ha senso starsi ad occupare di dividere ulteriormente questa sottocartella per le regioni linguistiche: 77 | ci sono moltissime cose preliinari, forse irrealizzabili. Ma se mai i tempi dovessero venire maturi, con questa struttura l'implementazione della localizzazione sarà possibile!_ 78 | 79 | -------------------------------------------------------------------------------- /LootBot/logic/craft.js: -------------------------------------------------------------------------------- 1 | // Gestisce la parte della logica dedicata al craft di oggetti (LootItems) 2 | const items_controller = require("./items"); 3 | const inventory_controller = require("./inventory"); 4 | const utils = require("../utility/utils"); // Le utilità 5 | 6 | const fixed_max_loops = 10000; // Numero massimo di iterazioni sulla linea craft. (nel caso venga raggiunto la linea craft è solo parziale) 7 | 8 | module.exports = { 9 | full_line_craft: full_line_craft, // Funzione pubblica per l'analisti della linea craft di un array di LootItems 10 | fixed_max_loops: fixed_max_loops 11 | } 12 | 13 | /* full_line_craft 14 | ingresso: 15 | toCraft_array: array di {id, quantity} 16 | player_inventory: lo zaino dell'utente 17 | preserve_crafted: un booleano. Se true non verranno consumati creati dallo zaino. 18 | output: risposta {…} 19 | > La funzione richiama process_recoursiveCraft per il calcolo dell'output 20 | */ 21 | async function full_line_craft(toCraft_array, player_inventory, preserve_crafted) { 22 | // TODO (??) <- (loops, skipped used_items.ids)response andrebbero messi in un oggetto separato (?) 23 | 24 | let response = { 25 | craft_point: 0, // Somma dei PC legit <- È usato per l'output 26 | craft_cost: 0, // Costo in edollari (legit) <- È usato per l'output 27 | missing_baseItems: [], // array di oggetti items.craftInfo <- È usato per l'output 28 | used_items: { // ? Opzionale 29 | ids: [], // array di ID oggetto <- È usato internamente per un controllo piu rapido 30 | base: [], // array di oggetti item_craftInfo <- È usato per l'output 31 | crafted: [] // array di oggetti item_craftInfo <- È usato per l'output 32 | }, 33 | manual_craft: [], 34 | loops: 0, // Intero, contatore sul numero di ripetizioni (o craft x1) <- Può essere usato per limitare l'utimlizzo della funzione multicraft 35 | skipped: [], // Array con gli itemId che non è stato possibile processare. Idealmente è sempre vuoto 36 | } 37 | 38 | let flatten_toCraftIDs_array = process_toCraftArray(toCraft_array); // preparo array degli id dei creati da realizzare 39 | let process_startDate = Date.now(); // per il log qui sotto 40 | process_recoursiveCraft(flatten_toCraftIDs_array, player_inventory, preserve_crafted, response); 41 | console.log(`> Linea craft generata in ${Date.now() - process_startDate}ms`); // log di test... non c'è ancora una classe log (test, errori…) 42 | check_if_is_partial(response); 43 | response.manual_craft = clean_manual_craft_list(response.manual_craft.reverse()); 44 | 45 | return response; // in response c'è tutto… 46 | } 47 | 48 | // Accessoria di full_line_craft() -> Restituisce un array degli id degli oggetti da creare ((id ripetuto N-volte la quantità)) 49 | function process_toCraftArray(toCraft_array) { 50 | let array_risultato = []; 51 | for (let i = 0; i < toCraft_array.length; i++) { 52 | for (let j = 0; j < toCraft_array[i].quantity; j++) { 53 | array_risultato.push(toCraft_array[i].id); 54 | } 55 | } 56 | return array_risultato; 57 | } 58 | 59 | /* process_recoursiveCraft 60 | la funzione utilizza la lista degli oggetti allItems confrontandola con quella dello zaino_utente 61 | ingresso: 62 | currDeep_array: array degli ID-oggetto nel livello di profondità (inizialmente il root_items.childIds_array) 63 | */ 64 | function process_recoursiveCraft(currdeep_array, player_inventory, preserve_zaino, response) { 65 | let nextdeep_array = []; 66 | // L'array della prossima chiamata a questa funzione. viene popolato (eventualmente) in craft_logic() 67 | 68 | currdeep_array.forEach((item_id) => { // Scorro la lista di id del livello attuale 69 | let tmp_item = items_controller.craftItemInfo_FromItemId(item_id); // L'oggetto per (id) 70 | if (utils.isNully(tmp_item)) { // Aggiorna l'array skipped (Male!) 71 | response.skipped.push(item_id); 72 | } else { // Passa il controllo alla logica del craft 73 | let fromInventory_item = inventory_controller.hasItem(item_id, player_inventory); // controllo di oggetto nello zaino utente (la quantità è sempre quella assoluta e può essere 0) 74 | craft_logic(tmp_item, fromInventory_item, nextdeep_array, preserve_zaino, response); 75 | } 76 | }); 77 | 78 | 79 | // Continuare con il loop o fermarsi? Dipende da craft_can_continue e cosa c'è in nextdeep_array… 80 | if (craft_can_continue(response) && nextdeep_array.length > 0) { // Ci sono id di oggetti ancora da valutare 81 | response.loops++; // Incremento il contatore di loops 82 | return process_recoursiveCraft(nextdeep_array, player_inventory, preserve_zaino, response); // Il ciclo ricomincia 83 | } else { 84 | return true; // Fine! (La funzione non restituisce nulla. Ha aggiornato durante i sui cicli i valori in response…) 85 | } 86 | 87 | } 88 | 89 | // la logica del craft (che viente riflessa nell'oggetto response). 90 | function craft_logic(item, fromInventory_item, nextdeep_array, preserve_zaino, response) { 91 | if (item.craftable == 1 && (response.loops == 0 || preserve_zaino == true)) { // Se è un creato e preserve_zaino == true passo i necessari al prossimo livello 92 | update_craft(item, nextdeep_array, response); // Manda ad accessoria (nextdeep_array e response vengono aggiornati) 93 | } else if (response.used_items.ids.indexOf(item.id) <= 0) { // L'oggetto NON è già tra quelli utilizzati fino a questo momento 94 | craft_logic_newUsed(item, fromInventory_item, nextdeep_array, response); 95 | } else { // È gia tra i consumati. 96 | craft_logic_alreadyUsed(item, fromInventory_item, nextdeep_array, response); 97 | } 98 | } 99 | 100 | // Accessoria di craft_logic() -> Inserisce i necessari per il creato in nextdeep_array ed aggiorna i contatori di response (craft_pnt, craft_cost) 101 | function update_craft(item, nextdeep_array, response) { 102 | if (item.craftable == 1){ 103 | for (let i = 0; i < response.manual_craft.length; i++) { 104 | if (response.manual_craft[i].id == item.id) { 105 | item.total_quantity += response.manual_craft[i].total_quantity; 106 | response.manual_craft.splice(i, 1); 107 | } 108 | } 109 | response.manual_craft.push(item); 110 | } 111 | response.craft_point += item.craft_pnt; 112 | response.craft_cost += item.craft_cost; 113 | nextdeep_array.push(...item.needed); 114 | } 115 | 116 | // Accessoria di craft_logic() -> gestisce la logica per oggetti (base o creati) usati per la prima volta all'interno della linea 117 | function craft_logic_newUsed(item, fromInventory_item, nextdeep_array, response) { 118 | if (item.craftable == 0) { 119 | // Per gli oggetti base eseguo sempre il controllo sullo zaino 120 | if (fromInventory_item.has_item == false) { // se non è presente aggiorno la lista dei base mancanti 121 | add_item_inList(response.missing_baseItems, item); 122 | } else { // se è presente 123 | add_item_inList(response.used_items.base, item); // aggiorno la lista dei consumati (base) 124 | response.used_items.ids.push(item.id); // aggiorno la lista degli id (consumati) 125 | } 126 | } else { // se l'oggetto è un creato (e l'utilizzo di creati dallo zaino è abilitato (controllo a monte in craft_logic)) 127 | if (fromInventory_item.has_item == false || fromInventory_item.quantity < 1) { // Non è presente o quantità insufficente 128 | update_craft(item, nextdeep_array, response); // -> aggiungo i necessari al prossimo livello ed aggiorno i contatori in response 129 | } else { // Aggiorno la lista degli oggetti usati. 130 | add_item_inList(response.used_items.crafted, item); 131 | response.used_items.ids.push(item.id); // ... ora anche :) 132 | } 133 | } 134 | } 135 | 136 | // Accessoria di craft_logic() -> gestisce la logica per oggetti (base o creati) GIÀ usati all'interno della linea 137 | function craft_logic_alreadyUsed(item, fromInventory_item, nextdeep_array, response) { 138 | // Per gli oggetti base 139 | if (item.craftable == 0) { // NON possono arrivare oggetti base non presenti nello zaino (vedi sopra) 140 | let used_item = response.used_items.base.filter((i_item) => i_item.id == item.id)[0]; // l'oggetto usato (base) in questione 141 | // controllo contro la quantità nello zaino 142 | if ((used_item.total_quantity + 1) > fromInventory_item.quantity) { 143 | add_item_inList(response.missing_baseItems, item); // Quantità insufficente, aggiorno lista dei necessari 144 | } else { 145 | add_item_inList(response.used_items.base, item); // Quantità sufficente, aggiorno lista dei consumati (base) 146 | } 147 | } else { // Per i creati 148 | let used_item = response.used_items.crafted.filter((i_item) => i_item.id == item.id)[0]; // l'oggetto usato (creato) in questione 149 | 150 | if ((used_item.total_quantity + 1) < fromInventory_item.quantity) { // Se la quantità nello zaino non è sufficente, aggiungo i necessari per il creato al nextdeep_arra 151 | update_craft(item, nextdeep_array, response); 152 | } else { // Altrimenti aggiorno la lista dei consumati (creati) 153 | add_item_inList(response.used_items.crafted, item); 154 | } 155 | 156 | } 157 | } 158 | 159 | // Accessoria di craft_logic() -> Prima della push() su una generica [lista] controlla se oggetto è presente (nella [lista]) (e nel caso aggiorna la quantità con +1 (senza push) ) 160 | function add_item_inList(list, item) { 161 | for (let i = 0; i < list.length; i++) { 162 | if (list[i].id == item.id) { 163 | list[i].total_quantity++; 164 | return; 165 | } 166 | } 167 | list.push(item); 168 | return; 169 | } 170 | 171 | 172 | // Funzione utilizabile per fermare una linea craft in base ai dati contenuti in response 173 | // ad esempio valutando il numero del contatore loops o se il numero di copie di oggetti base supera "config.items_cap" (<- NOTA: al momento non esiste un oggetto che rappresenti globalmente il cap al numero di copie di un oggetto) 174 | function craft_can_continue(response) { 175 | return response.loops <= fixed_max_loops; // Attualmente il controllo è solo su fixed_max_loops 176 | } 177 | 178 | function check_if_is_partial(response){ 179 | if (response.loops >= fixed_max_loops){ 180 | response = { 181 | partial: response, 182 | }; 183 | } 184 | } 185 | 186 | function clean_manual_craft_list(manual_craft_list){ 187 | let clean_list = []; 188 | 189 | manual_craft_list.forEach(item => { 190 | clean_list.push({id: item.id, quantity: item.total_quantity}); 191 | }); 192 | return clean_list; 193 | 194 | } -------------------------------------------------------------------------------- /LootBot/logic/inventory.js: -------------------------------------------------------------------------------- 1 | // Gestisce la parte della logica dedicata agli zaini (inventories) 2 | const model = require("../model/DB_managers/specific/inventory"); // Il modello che cura l'interazione con il database (per le specifiche su inventory) 3 | //const view = require("../views/"); // Non sono affatto sicuro sia un buon modo di gestire le stringhe… 4 | 5 | module.exports = { 6 | complete: model.complete, // Restituisce lo zaino completo di un giocatore 7 | update_items_quantityOf: model.items.update_quantities_Of, 8 | hasItem: inventory_hasItem, // Controlla se in uno zaino è presente un oggetto (LootItem) 9 | inventory_cap: inventory_cap, 10 | } 11 | 12 | function inventory_cap() { 13 | const affected_rarities = { 14 | C: 6000, 15 | NC: 4500, 16 | R: 3000, 17 | UR: 2000, 18 | L: 1000, 19 | E: 1000, 20 | UE: 500, 21 | X: 500, 22 | U: 500, 23 | }; 24 | 25 | return { 26 | get_rarity: () => Object.keys(affected_rarities), 27 | get_cap_forRarity: (rarity) => affected_rarities[rarity] ? affected_rarities[rarity] : true, 28 | cap_check: (rarity, quantity) => { 29 | console.log(rarity, quantity) 30 | const cap = affected_rarities[rarity]; 31 | return (cap && quantity < cap); 32 | }, 33 | }; 34 | } 35 | 36 | // controlla se nello zaino è presente oggetto, non saprei cosa altro aggiungere al commento. 37 | function inventory_hasItem(item_id, player_inventory) { 38 | const item = player_inventory.find((item) => item.item_id === parseInt(item_id)); 39 | return { 40 | has_item: (Boolean(item) && item.quantity > 0), 41 | quantity: item ? item.quantity : -1, 42 | }; 43 | } 44 | 45 | /* Metto in pausa il portin di queste funzioni. C'è troppa roba e va divisa diversamente… 46 | 47 | 48 | function equip_hasItem(item_id, player_equip){ 49 | return player_equip.some(equipitem_id => Object.values(equipitem_id).includes(item_id)); 50 | } 51 | 52 | async function get_playerEquip(player_id){ 53 | let response = { // c'è tanta roba in player per l'equip... metto in pausa queste funzioni 54 | esit: false, 55 | equip: { 56 | sword: -1, 57 | armor: -1, 58 | shield: -1 59 | } 60 | } 61 | 62 | let raw_data= await model.equip.complete(player_id); 63 | } 64 | 65 | async function durability_controll_onAdd(player_id, item_durability,){ 66 | let response = { 67 | update_durability: false, 68 | update_max_durability: flase 69 | } 70 | 71 | if (item_durability == null) { 72 | 73 | let equip = await model.equip.complete(player_id); 74 | 75 | var rows = await connection.queryAsync('SELECT weapon_id, weapon2_id, weapon3_id FROM player WHERE id = ' + player_id); 76 | var weapon_id = rows[0].weapon_id; 77 | var weapon2_id = rows[0].weapon2_id; 78 | var weapon3_id = rows[0].weapon3_id; 79 | var rows = await connection.queryAsync('SELECT power, power_armor, power_shield, rarity FROM item WHERE id = ' + item_id); 80 | 81 | if ((rows[0].power > 0) || (rows[0].power_armor < 0) || (rows[0].power_shield < 0)) { 82 | var item_durability = getDurability(rows[0].rarity); 83 | var rows = await connection.queryAsync('SELECT quantity FROM inventory WHERE player_id = ' + player_id + ' AND item_id = ' + item_id); 84 | // se non avevo copie dell'oggetto e non è equipaggiato, imposta la durabilità massima, altrimenti mantieni quella attuale 85 | if (((Object.keys(rows).length == 0) || (rows[0].quantity == 0)) && (weapon_id != item_id) && (weapon2_id != item_id) && (weapon3_id != item_id)) 86 | durability_query = ", durability = " + item_durability + ', durability_max = ' + item_durability; 87 | } 88 | } else 89 | durability_query = ", durability = " + item_durability; 90 | } 91 | 92 | async function addItem(player_id, item_id, item_quantity = 1, item_durability = null, item_iscollected = true) { 93 | let response = { 94 | esit: false, 95 | message_text: "" 96 | } 97 | 98 | item_quantity = parseInt(item_quantity); 99 | if (isNaN(item_quantity)) { 100 | let function_paparameters = [{player_id: player_id, item_id: item_id, item_quantity: item_quantity}] 101 | response.message_text = view.print(view.logic.error.item_quantity, view.flatted_function_paparameters(function_paparameters)) 102 | return response; 103 | } 104 | 105 | if (item_id == 646){ 106 | // gestione polvere? 107 | } 108 | 109 | var durability_query = ""; 110 | 111 | var exclude_items = [646]; 112 | if (!exclude_items.includes(item_id)) { 113 | var inv_quantity = getItemCnt(player_id, item_id); 114 | if (inv_quantity >= 1000) 115 | return; 116 | 117 | if (inv_quantity+qnt >= 1000) { 118 | qnt = 1000-inv_quantity; 119 | return; 120 | } 121 | } 122 | 123 | var collected_qnt = item_quantity; 124 | if (!item_iscollected) 125 | collected_qnt = 0; 126 | 127 | var rows = await connection.queryAsync('UPDATE inventory SET quantity = quantity+' + item_quantity + durability_query + ', collected = collected+' + collected_qnt + ' WHERE player_id = ' + player_id + ' AND item_id = ' + item_id); 128 | if (rows.affectedRows == 0) 129 | await connection.queryAsync('INSERT INTO inventory (player_id, item_id, quantity) VALUES (' + player_id + ',' + item_id + ', ' + item_quantity + ')'); 130 | } 131 | 132 | */ -------------------------------------------------------------------------------- /LootBot/logic/items.js: -------------------------------------------------------------------------------- 1 | // Gestisce la parte della logica dedicata agli oggetti (LootItems) 2 | const model = require("../model/DB_managers/specific/items"); // Il modello che cura l'interazione con il database (per le specifiche su LootItems) 3 | const utils = require("../utility/utils"); // Le utilità 4 | 5 | let lootItems_array = []; // L'oggetto che custodisce [LootItems], caricato in ram all'avvio del bot (evita tutte le chiamate alla tabella item e craft) 6 | 7 | module.exports = { 8 | lootItems_array: lootItems_array.slice(), // restituisce una copia di lootItems (avrà vita solo per la chiamata) 9 | init: load_in_ram, // Inizzializza il modulo caricando gli oggetti da item e craft 10 | 11 | // ******** Funzioni generiche 12 | item_infos: item_infos_fromID, // Tutte le informazioni item_infos per un item_id 13 | search_craftable_item: craftable_infos_fromName, 14 | 15 | // ******** Funzioni oggetti creati 16 | craftables_ofRarity: craftables_list_fromRarity, // restituisce tutti i creabili di una rarità 17 | all_craftables_forReborn: all_craftables_forReborn, // Lista dei creabili, data una rinascita 18 | craftables_ofRarities: craftables_list_fromRarities, // Lista dei creabili, data una rarità 19 | get_craftable_array_groupedIndexes: get_craftable_array_groupedIndexes, // restituisce un array con le iniziali usate nei creati 20 | get_all_craftable_rarity: get_all_craftable_rarity, // Lista delle rarità che hanno almeno un creato 21 | 22 | // ******** Funzioni per il Craft 23 | craftItemInfo_FromItemId: craft_item_info_From, // craft_item_info usato nella logica del craft (l'oggetto item_info in [allItems_array], semplificato) 24 | } 25 | 26 | // ******** INIT 27 | // L'init di oggetti va chiamato all'avvio del bot (carica in ram l'albero oggetti, così che l'accesso sia piu rapido) 28 | async function load_in_ram() { 29 | try { 30 | 31 | let inizio = Date.now(); // rimuovi: solo per test iniziale 32 | let db_items = await model.loadAllItems(); 33 | 34 | if (db_items === false) 35 | return; 36 | 37 | await normalize_itemsRawData(db_items, lootItems_array); // carica i necessari dalla tabella craft 38 | // console.log(`> Tempo items.init -> ${Date.now() - inizio}ms`); 39 | 40 | return lootItems_array.length; 41 | } 42 | catch (error) { 43 | console.error(error) 44 | } 45 | } 46 | 47 | // Funzione usata nell'inizializzazione del modulo: 48 | // Normalizza ed effetivamente inizializza l'array pubblico lootItems 49 | async function normalize_itemsRawData(raw_data, target_array) { 50 | if (!utils.isNully(raw_data)) { // grazie furins 51 | for (let i = 0; i < raw_data.length; i++) { 52 | if (raw_data[i][utils.db_structures.items.craftable] === 1) { // per i creabili calcolo ed aggiungo: 53 | let item_info = craftInfo_fromRarity(raw_data[i][utils.db_structures.items.rarity]); // > costo craft e pc 54 | raw_data[i].craft_pnt = item_info.craft_pnt; 55 | raw_data[i].craft_cost = item_info.craft_cost; 56 | 57 | let craft_needed_materials = await model.load_neededOf(raw_data[i][utils.db_structures.items.key]); // oggetti necessari 58 | 59 | if (craft_needed_materials === false || typeof craft_needed_materials === "undefined") { 60 | raw_data[i].isSterile = true; 61 | } else { // estrapolo l'oggetto e lo normalizzo 62 | raw_data[i].isSterile = false; 63 | raw_data[i].needed = [ // i parseInt è da abbastanza paranoico :( 64 | parseInt(craft_needed_materials[utils.db_structures.craft.needed[1]]), 65 | parseInt(craft_needed_materials[utils.db_structures.craft.needed[2]]), 66 | parseInt(craft_needed_materials[utils.db_structures.craft.needed[3]]) 67 | ]; 68 | } 69 | } 70 | 71 | target_array.push(raw_data[i]); // Aggiungo a target_array 72 | } 73 | return true; 74 | } 75 | return false; 76 | } 77 | 78 | // ******** ACCESSORIE GENERICHE 79 | 80 | function item_infos_fromID(item_id) { 81 | return lootItems_array.find((item) => (item.id == item_id)); 82 | } 83 | 84 | function craftable_infos_fromName(item_partial_name) { 85 | return lootItems_array.filter((item) => (item.craftable== 1 && item.name.toLowerCase().match(item_partial_name.toLowerCase())) 86 | ); 87 | } 88 | 89 | // ******** ACCESSORIE CRAFTABLE = 1 90 | 91 | function all_craftables() { 92 | return lootItems_array.filter(item => item.craftable === 1); 93 | } 94 | 95 | function all_craftables_forReborn(player_reborn) { 96 | return lootItems_array.filter((item) => (item.craftable == 1 && item.reborn <= parseInt(player_reborn))); 97 | } 98 | 99 | // I creati di una rarità, se reborn è specificato il filtro si estende alla rinascita 100 | function craftables_list_fromRarity(item_rarity, player_reborn = false) { 101 | return lootItems_array.filter((item) => player_reborn === false ? (item.craftable == 1 && item.rarity == item_rarity) : item.craftable == 1 && item.rarity == item_rarity && item.reborn <= parseInt(player_reborn)); 102 | } 103 | 104 | // I creati, ma da un array di rarità 105 | function craftables_list_fromRarities(rarity_array) { 106 | return lootItems_array.filter((item) => (item.craftable == 1 && rarity_array.indexOf(item.rarity) >= 0)); 107 | } 108 | 109 | 110 | // restituisce un array delle iniziali usate per i creati nell'array craftable_array 111 | function get_craftable_array_prefixes(craftable_array) { 112 | const initialsArray = []; 113 | 114 | for (let i = 0; i < craftable_array.length; i++) { 115 | const initials = craftable_array[i].name.charAt(0).toUpperCase(); 116 | if (initialsArray.indexOf(initials) === -1) { 117 | initialsArray.push(initials); 118 | } 119 | } 120 | 121 | return initialsArray.sort(); 122 | } 123 | 124 | // restituisce un array delle iniziali usate per i creati nell'array craftable_array 125 | // come get_craftable_array_prefixes, ma raggruppa le lettere (prefissi) con pochi elementi (fixed_minimum) 126 | function get_craftable_array_groupedIndexes(craftable_array) { 127 | let fixed_minimum = 5; 128 | let indexes_array = get_craftable_array_prefixes(craftable_array); 129 | const groupedIndexes = []; 130 | let count = 0; 131 | let group = ''; 132 | 133 | for (let i = 0; i < indexes_array.length; i++) { 134 | const currentIndex = indexes_array[i]; 135 | const occurrences = craftable_array.reduce((occurrences_counter, item_info) => { 136 | if (item_info.name.charAt(0).toUpperCase() === currentIndex) { 137 | occurrences_counter++; 138 | } 139 | return occurrences_counter; 140 | }, 0); 141 | 142 | count += occurrences; 143 | group += currentIndex; 144 | 145 | if (count >= fixed_minimum || i === indexes_array.length - 1) { 146 | groupedIndexes.push(group); 147 | group = ''; 148 | count = 0; 149 | } 150 | } 151 | 152 | return groupedIndexes; 153 | } 154 | 155 | 156 | // Restituisce l'array delle rarità che hanno almeno un creabile 157 | function get_all_craftable_rarity(player_reborn = false) { 158 | const filteredItems = player_reborn === false ? all_craftables() : all_craftables_forReborn(player_reborn); 159 | const raritiesArray = filteredItems.map(item => item.rarity); 160 | const uniqueRarities = raritiesArray.filter((rarity, index) => raritiesArray.indexOf(rarity) === index); 161 | return uniqueRarities; 162 | } 163 | 164 | // ******** ACCESSORIE CRAFT 165 | 166 | // Restituisce craft_cost e craft_pnt in funzione di rarity 167 | function craftInfo_fromRarity(rarity) { 168 | switch (rarity) { 169 | case "NC": { 170 | return ({ craft_cost: 2000, craft_pnt: 0 }); 171 | } case "R": { 172 | return ({ craft_cost: 3000, craft_pnt: 1 }); 173 | } case "UR": { 174 | return ({ craft_cost: 5000, craft_pnt: 2 }); 175 | } case "L": { 176 | return ({ craft_cost: 7500, craft_pnt: 3 }); 177 | } case "E": { 178 | return ({ craft_cost: 10000, craft_pnt: 5 }); 179 | } case "UE": { 180 | return ({ craft_cost: 100000, craft_pnt: 25 }); 181 | } case "U": { 182 | return ({ craft_cost: 250000, craft_pnt: 35 }); 183 | } case "X": { 184 | return ({ craft_cost: 1000000, craft_pnt: 50 }); 185 | } case "S": { 186 | return ({ craft_cost: 50000, craft_pnt: 15 }); 187 | } default: { 188 | return ({ craft_cost: 0, craft_pnt: 0 }); 189 | } 190 | } 191 | } 192 | 193 | // L'oggetto item usato per il full_line_craft 194 | function craft_item_info_From(itemID) { 195 | const full_item_info = lootItems_array.find((item) => item.id === parseInt(itemID)); 196 | 197 | if (!full_item_info) { 198 | return null; // Restituisce null se l'oggetto non è presente nell'array lootItems_array 199 | } 200 | 201 | const craft_item_info = { 202 | id: full_item_info.id, 203 | name: full_item_info.name, 204 | rarity: full_item_info.rarity, 205 | craftable: full_item_info.craftable, 206 | craft_pnt: full_item_info.craft_pnt, 207 | craft_cost: full_item_info.craft_cost, 208 | total_quantity: 1, 209 | needed: Array.isArray(full_item_info.needed) && full_item_info.needed.length === 3 ? full_item_info.needed : [], 210 | 211 | } 212 | 213 | return craft_item_info 214 | } -------------------------------------------------------------------------------- /LootBot/logic/master_craftsman.js: -------------------------------------------------------------------------------- 1 | const model = require("../model/JSON_managers/specifiche/master_craftsman"); 2 | const error_views = require("../views/errors"); 3 | 4 | const utils = require("../utility/utils"); 5 | 6 | const craft_logics = require("./craft"); // Logica per i craft 7 | const item_logics = require("./items"); 8 | const inventory_logics = require("./inventory"); 9 | const player_logics = require("./players"); 10 | 11 | 12 | module.exports = { 13 | get_craftsman_info: get_craftsman_info, 14 | update_craftsman_info: update_craftsman_info, 15 | 16 | clear_craftsman_info: clear_craftsman_info, 17 | list_total_quantity: list_total_quantity, 18 | add_item_to_items_list: add_item_to_items_list, 19 | add_itemAndQuantity_to_items_list: add_itemAndQuantity_to_items_list, 20 | generate_query_random_char: generate_query_random_char, 21 | validate_items_list_forReborn: validate_items_list_forReborn, 22 | 23 | item_infos_forList: item_infos_forList, 24 | search_item: search_item, 25 | item_infos_forList_craftable_check: item_infos_forList_with_craftable_check, 26 | filter_list_for_inventory: filter_list_for_inventory, 27 | 28 | pleyer_info_controll: pleyer_info_controll, 29 | pleyer_inventory_controll: pleyer_inventory_controll, 30 | player_craft_abilities: player_craft_abilities, 31 | player_assault_infos: player_assault_infos, 32 | player_assault_upgrade_needs: player_assault_upgrade_needs, 33 | player_smuggler_offert: player_smuggler_current_offert, 34 | 35 | 36 | validate_can_proceed: validate_can_proceed, 37 | craft_line_controll: craft_line_controll, 38 | commit_craft: commit_craft, 39 | craftman_info_craftUpdate: craftman_info_craftUpdate, 40 | 41 | 42 | // Vista 43 | craf_line_error: craf_line_error, 44 | item_infos: item_infos, 45 | get_craftables_ofRarity: get_craftables_ofRarity, 46 | sort_items_fromRarity: sort_items_fromRarity, 47 | get_craftables_forRebor: get_craftables_forRebor, 48 | get_craftable_subset_fromPrefixes: get_craftable_subset_fromPrefixes, 49 | get_craftable_array_groupedPrefixes: get_craftable_array_groupedPrefixes, 50 | get_prefixes_ofRarity: get_prefixes_ofRarity, 51 | is_craftable_rarity: is_craftable_rarity, 52 | avaible_rarities: avaible_rarities 53 | } 54 | 55 | 56 | 57 | 58 | 59 | // ************************** ITEM INFOS 60 | // TODO generico: quasi tutte le funzioni possono essere accorpate o riscritte... c'è molta ridondanza... 61 | 62 | 63 | // Informazioni complete su un oggetto item. In ingresso "item" può essere o un oggetto {id, quantità} o solo una stringa|intero (itemID) 64 | function item_infos(item) { 65 | return item_logics.item_infos(item.id ? item.id : parseInt(item)); 66 | } 67 | 68 | // item_infos, ma partendo da una lista di {id, quantity} 69 | function item_infos_forList(list) { 70 | return list.map((item) => { 71 | let item_info = item_infos(item); 72 | if (item.quantity) { 73 | item_info.quantity = item.quantity; 74 | } 75 | return item_info; 76 | }); 77 | } 78 | 79 | // item_infos(), ma sul nome oggetto 80 | function search_item(item_partial_name, item_list) { 81 | return item_list.filter((item) => (item.name.toLowerCase().match(item_partial_name.toLowerCase()))) 82 | } 83 | 84 | // item_infos, ma con controllo su "craftable" e partendo da una lista di {item_id, quantity} 85 | function item_infos_forList_with_craftable_check(list) { 86 | return list.map((item) => { 87 | let item_info = item_infos(item.item_id); 88 | 89 | if (item.quantity) { 90 | item_info.quantity = item.quantity; 91 | } else { 92 | item_info.quantity = 1; 93 | } 94 | 95 | if (item_info.craftable == 1) { 96 | return item_info; 97 | } else { 98 | return null; 99 | } 100 | }).filter((item) => item !== null); 101 | } 102 | 103 | // Controlla lo zaino del giocatore e filtra la lista item_list escludendo le quantità già presenti. (restituisce quindi una lista di mancanti) 104 | function filter_list_for_inventory(item_list, player_inventory) { 105 | let result = { 106 | filtered_list: [], 107 | quantity_list: [] 108 | } 109 | item_list.forEach(item => { 110 | let item_check = inventory_logics.hasItem(item.id, player_inventory); 111 | 112 | if (item_check.has_item == false) { 113 | result.filtered_list.push(item); 114 | } else { 115 | result.quantity_list.push({ id: item.id, quantity: item_check.quantity, name: item.name, rarity: item.rarity }); 116 | if (item_check.quantity < item.quantity) { 117 | result.filtered_list.push({ id: item.id, quantity: item.quantity - item_check.quantity, name: item.name, rarity: item.rarity }); 118 | } 119 | } 120 | }); 121 | 122 | return result; 123 | } 124 | 125 | 126 | // ************************** CRAFT 127 | 128 | // Il craft effettivo (Tutti i controlli del caso... infondo agli if la query sul database) 129 | async function commit_craft(craftsman_info, player_info) { 130 | let response = { 131 | money_controll: false, 132 | load_controll: false, 133 | used_items_controll: true, 134 | target_items_controll: true, 135 | } 136 | 137 | let craft_report = { 138 | craft_cost: craftsman_info.controll.craft_cost, 139 | craft_gained_pc: craftsman_info.controll.craft_point, 140 | used_items: [], 141 | crafted_items: [] 142 | }; 143 | let update_array = []; // [player_Id, item_Id, new_quantity] 144 | let player_inventory_controll; 145 | let player_inventory; 146 | let all_used_items; 147 | 148 | // controllo su gruzzolo, le informazioni di player_info sono già aggiornate 149 | response.money_controll = (!isNaN(craftsman_info.controll.craft_cost) && player_info.money > parseInt(craftsman_info.controll.craft_cost)) 150 | 151 | // Carico zaino 152 | player_inventory_controll = await inventory_logics.complete(player_info.id); 153 | response.load_controll = player_inventory_controll.esit 154 | if (!(response.load_controll && response.money_controll)) { 155 | return response; 156 | } 157 | 158 | player_inventory = player_inventory_controll.player_inventory; // lo zaino fresco fresco 159 | all_used_items = [...craftsman_info.controll.used_items.base, ...craftsman_info.controll.used_items.crafted]; 160 | 161 | // QUANTITÀ DECREMENTATE: per ogni oggetto usato controllo che inventory.quantity >= a used_item.total_quantity (e che ci sia ancora...) 162 | for (used_item of all_used_items) { 163 | let inventory_item = inventory_logics.hasItem(used_item.id, player_inventory); 164 | 165 | if (!inventory_item.has_item || inventory_item.quantity < parseInt(used_item.total_quantity)) { 166 | response.used_items_controll = false; 167 | break; 168 | } else { 169 | let new_quantity = (inventory_item.quantity - parseInt(used_item.total_quantity)); 170 | update_array.push([player_info.id, used_item.id, new_quantity]); 171 | craft_report.used_items.push({ item_id: used_item.id, new_quantity: new_quantity, after_craft_quantity: used_item.total_quantity }) 172 | } 173 | }; 174 | if (!response.used_items_controll) { 175 | return response; 176 | } 177 | 178 | // QUANTITÀ INCREMENTATE: per ogni oggetto creato controllo che quantity <= cap(rarity) 179 | for (target_item of craftsman_info.controll.target_items_list) { 180 | let already_in_list_index = update_array.findIndex((item) => (item[1] == target_item.id)); 181 | let new_quantity = 0; 182 | 183 | if (already_in_list_index >= 0) { 184 | new_quantity = update_array[already_in_list_index][2] + parseInt(target_item.total_quantity); 185 | // Controllo sul cap oggetti 186 | let cap_check = inventory_logics.inventory_cap().cap_check(target_item.rarity, new_quantity); 187 | if (!cap_check) { 188 | response.target_items_controll = false; 189 | break; 190 | } 191 | update_array[already_in_list_index][2] = new_quantity 192 | } else { 193 | let inventory_item = inventory_logics.hasItem(target_item.id, player_inventory); 194 | new_quantity = inventory_item.has_item ? (inventory_item.quantity + parseInt(target_item.total_quantity)) : parseInt(target_item.total_quantity); 195 | update_array.push([player_info.id, target_item.id, new_quantity]); 196 | } 197 | 198 | craft_report.crafted_items.push({ item_id: target_item.id, new_quantity: new_quantity, after_craft_quantity: target_item.total_quantity }) 199 | 200 | }; 201 | 202 | if (!response.target_items_controll) { 203 | return response; 204 | } 205 | 206 | 207 | response.craft_report = craft_report; 208 | response.update_quantity = await inventory_logics.update_items_quantityOf([update_array]); 209 | response.update_money = await player_logics.money.decrease((craftsman_info.controll.craft_cost) * utils.master_craftsman_cost_multiplier, player_info.account_id); 210 | response.update_craft_point = await player_logics.increase_player_craft_point(craftsman_info.controll.craft_point, player_info.account_id); 211 | 212 | 213 | 214 | return response; 215 | } 216 | 217 | //Booleano: Le condizioni di validazione lista 218 | function validate_can_proceed(craft_line, player_info) { 219 | return ( 220 | parseInt(craft_line.craft_cost) * utils.master_craftsman_cost_multiplier < utils.player_max_money && // forse in questo caso la lista andrebbe semplicemente stralciata... 221 | parseInt(craft_line.craft_cost) * utils.master_craftsman_cost_multiplier <= player_info.money && 222 | craft_line.missing_baseItems.length <= 0 && 223 | (craft_line.used_items.base.length + craft_line.used_items.crafted.length) > 0 224 | ); 225 | } 226 | 227 | // controlli sulla linea già creata 228 | async function craft_line_controll(player_info, craftsman_info, player_inventory) { 229 | let response = { 230 | has_error: false, 231 | is_incompleate: false, 232 | is_too_expensive: false, 233 | craft_line: {} 234 | } 235 | 236 | let craft_line = await craft_logics.full_line_craft(craftsman_info.items_list, player_inventory, craftsman_info.preserve_crafted); 237 | 238 | if (utils.isNully(craft_line) || craft_line.loops <= 0 || craft_line.used_items.ids.length <= 0 || craft_line.skipped.length > 0 239 | ) { // La linea craft non è stata generata correttamente... 240 | response.has_error = true; 241 | } else if (craft_line.loops > craft_logics.fixed_max_loops) { 242 | response.is_incompleate = true; 243 | clear_craftsman_info(craftsman_info); 244 | await update_craftsman_info(player_info.account_id, craftsman_info); 245 | } else if (parseInt(craft_line.craft_cost) * utils.master_craftsman_cost_multiplier > utils.player_max_money) { 246 | response.is_incompleate = true; 247 | clear_craftsman_info(craftsman_info); 248 | await update_craftsman_info(player_info.account_id, craftsman_info); 249 | } 250 | 251 | response.craft_line = craft_line; 252 | return response; 253 | } 254 | 255 | // Aggiornamento del file craftman_info 256 | function craftman_info_craftUpdate(craftsman_info, craft_line, message_id) { 257 | let target_items_list = []; 258 | craftsman_info.items_list.forEach((item) => { 259 | let item_info = item_logics.item_infos(item.id); 260 | if (item_info) { 261 | target_items_list.push({ id: item.id, total_quantity: item.quantity, rarity: item_info.rarity }); 262 | } 263 | }) 264 | craftsman_info.query_random_char = generate_query_random_char(); 265 | craftsman_info.current_message_id = message_id; 266 | craftsman_info.controll = { 267 | target_items_list: target_items_list, 268 | controll_date: Date.now(), 269 | loops: craft_line.loops, 270 | craft_point: craft_line.craft_point, 271 | craft_cost: craft_line.craft_cost, 272 | missing_baseItems: craft_line.missing_baseItems, 273 | used_items: { 274 | base: craft_line.used_items.base, 275 | crafted: craft_line.used_items.crafted 276 | }, 277 | manual_craft: craft_line.manual_craft, 278 | skipped: [], 279 | } 280 | 281 | } 282 | 283 | 284 | // ************************** LOAD ESTERNI 285 | 286 | //Carico playerinfo 287 | async function pleyer_info_controll(telegram_user_id) { 288 | return await player_logics.player_full_infos(telegram_user_id) 289 | } 290 | 291 | async function player_craft_abilities(player_id){ 292 | let all_abilities = await player_logics.player_abilities(player_id); 293 | if (all_abilities.esit == false){ 294 | return {craft_max_quantities: 3}; 295 | } 296 | 297 | let craft_quantity_ability = all_abilities.results.find(ability => ability.ability_id == 21); 298 | if (!craft_quantity_ability || isNaN(craft_quantity_ability.ability_level) || isNaN(craft_quantity_ability.ability_power)){ 299 | return {craft_max_quantities: 3}; 300 | } 301 | return { 302 | craft_max_quantities: 3+parseInt(craft_quantity_ability.ability_level)*parseFloat(craft_quantity_ability.ability_power) 303 | } 304 | } 305 | 306 | async function player_assault_infos(player_id) { 307 | return await player_logics.assault.infos(player_id); 308 | } 309 | 310 | async function player_assault_upgrade_needs(team_id, place_id) { 311 | return await player_logics.assault.items_needed(team_id, place_id); 312 | } 313 | 314 | async function player_smuggler_current_offert(player_info) { 315 | return await player_logics.smuggler.current_offert(player_info.id); 316 | } 317 | 318 | //Carico player_inventory 319 | async function pleyer_inventory_controll(player_id) { 320 | return await inventory_logics.complete(player_id); 321 | } 322 | 323 | 324 | 325 | 326 | // ************************** PREFISSI ED INDICI RARITA 327 | 328 | // Estrae da craftable_array gli oggetti che iniziano per uno dei caratteri di prefixes_array 329 | function get_craftable_subset_fromPrefixes(prefixes_array, craftables_array) { 330 | return craftables_array.map(item_info => ({ name: item_info.name, id: item_info.id })).filter(item => prefixes_array.indexOf(item.name.charAt(0).toUpperCase()) >= 0).sort((a, b) => a.name.localeCompare(b.name)); 331 | } 332 | 333 | // restituisce un array delle iniziali usate per i creati nell'array craftable_array 334 | // come get_craftable_array_prefixes, ma raggruppa le lettere (prefissi) con pochi elementi (fixed_minimum) 335 | function get_craftable_array_groupedPrefixes(craftable_array) { 336 | let indexes_array = get_craftable_array_prefixes(craftable_array); 337 | 338 | let grouped_prefixes = []; 339 | 340 | let fixed_maximum = 8; 341 | let fixed_minimum = 4; 342 | let fixed_absolute_maximum = 10; 343 | 344 | 345 | 346 | let tmp_occurrences_counter = 0; 347 | let tmp_prew_occurrences = 0; 348 | let tmp_group_string = ''; 349 | 350 | 351 | for (let i = 0; i < indexes_array.length; i++) { 352 | let currentIndex = indexes_array[i]; 353 | let occurrences = craftable_array.filter(item_info => item_info.name.charAt(0).toUpperCase() === currentIndex).length; 354 | 355 | tmp_occurrences_counter += occurrences; 356 | tmp_group_string += currentIndex; 357 | 358 | if (tmp_occurrences_counter >= fixed_maximum || i === indexes_array.length - 1) { 359 | if (grouped_prefixes.length === 0) { 360 | grouped_prefixes.push(tmp_group_string); 361 | } else { 362 | if (i === indexes_array.length - 1 && 363 | occurrences < fixed_minimum && 364 | tmp_prew_occurrences + occurrences <= fixed_absolute_maximum + fixed_minimum 365 | ) { 366 | grouped_prefixes[grouped_prefixes.length - 1] += tmp_group_string; 367 | } else { 368 | tmp_prew_occurrences = occurrences; 369 | grouped_prefixes.push(tmp_group_string); 370 | } 371 | } 372 | tmp_group_string = ''; 373 | tmp_occurrences_counter = 0; 374 | } 375 | } 376 | 377 | return grouped_prefixes; 378 | } 379 | 380 | // restituisce un array delle iniziali usate per i creati nell'array craftable_array 381 | function get_craftable_array_prefixes(craftable_array) { 382 | return craftable_array 383 | .map(item => item.name.charAt(0).toUpperCase()) 384 | .filter((initials, index, arr) => arr.indexOf(initials) === index) 385 | .sort(); 386 | } 387 | 388 | function get_craftables_ofRarity(item_rarity, player_reborn) { 389 | return item_logics.craftables_ofRarity(item_rarity, player_reborn); 390 | } 391 | 392 | 393 | 394 | // ************************** GESTIONE PERSISTENZA (SET e GET) 395 | 396 | // Funzione pubblica per creare una nuova lista craft. (se ne esiste già una sarà sovrascritta) 397 | async function new_craftsman_info(telegram_user_id) { 398 | let craftman_info = model.template.craftman_info; 399 | craftman_info.query_random_char = model.new_query_random_char(); 400 | return await model.update_craftsman_info(telegram_user_id, craftman_info); 401 | } 402 | 403 | // Espone il contenuto di ../sources/players/telegram_user_id/craftsman_info.json 404 | async function get_craftsman_info(telegram_user_id) { 405 | let craftsman_info = await model.get_craftsman_info(telegram_user_id); 406 | if (craftsman_info == false) { // potrebbe pur essere che non sia stato mai creato… 407 | craftsman_info = await new_craftsman_info(telegram_user_id); 408 | if (craftsman_info == false) { // niente.. proprio non si riesce ad accedervi. Male! 409 | return { 410 | esit: false, 411 | message_text: `${error_views.str.title}\n${error_views.print(error_views.str.players.load_craftsman_info, telegram_user_id)}, ${error_views.str.contact_admin}` 412 | }; 413 | } 414 | } 415 | return { 416 | esit: true, 417 | craftsman_info: craftsman_info 418 | }; 419 | } 420 | 421 | // Aggiorna il file json locale 422 | async function update_craftsman_info(telegram_user_id, new_craftsman_info) { 423 | return await model.update_craftsman_info(telegram_user_id, new_craftsman_info); 424 | } 425 | 426 | // Esegue il conteggio di quantity in items_list 427 | function list_total_quantity(items_list) { 428 | if (!items_list || items_list.length < 1) { 429 | return 0; 430 | } 431 | 432 | return items_list.reduce((total, item_info) => (total + parseInt(item_info.quantity)), 0); 433 | } 434 | 435 | // Aggiunge in maniera controllata un id oggetto alla lista item_list 436 | function add_item_to_items_list(item_id, item_list) { 437 | for (let i = 0; i < item_list.length; i++) { 438 | if (item_list[i].id == item_id) { 439 | item_list[i].quantity = parseInt(item_list[i].quantity) + 1; 440 | return item_list[i].quantity; 441 | } 442 | } 443 | 444 | item_list.push({ 445 | id: item_id, 446 | quantity: 1 447 | }); 448 | return 1; 449 | } 450 | 451 | // questa dovrebbe essere accorpata a quella sopra... <- TODO 452 | function add_itemAndQuantity_to_items_list(item, item_list) { 453 | for (let i = 0; i < item_list.length; i++) { 454 | if (item_list[i].id == item.id) { 455 | item_list[i].quantity = parseInt(item_list[i].quantity) + item.quantity; 456 | return item_list[i].quantity; 457 | } 458 | } 459 | 460 | item_list.push({ 461 | id: item.id, 462 | quantity: item.quantity 463 | }); 464 | 465 | return item.quantity; 466 | } 467 | 468 | // Reset di craftsman_info 469 | function clear_craftsman_info(craftsman_info) { 470 | craftsman_info.query_random_char = generate_query_random_char(); 471 | craftsman_info.items_list = []; 472 | 473 | craftsman_info.controll.controll_date = -1; 474 | craftsman_info.controll.craft_point = 0; 475 | craftsman_info.controll.craft_cost = 0; 476 | craftsman_info.controll.missing_baseItems = []; 477 | craftsman_info.controll.used_items = { base: [], crafted: [] }; 478 | craftsman_info.controll.skipped = []; 479 | } 480 | 481 | // Se restituisce qualche cosa e non un array vuoto, allora la validazione non è passata 482 | function validate_items_list_forReborn(items_list, player_reborn) { 483 | let available_craftables = get_craftables_forRebor(player_reborn); 484 | return items_list.filter((item) => !available_craftables.some((item_info) => item_info.id === parseInt(item.id))); 485 | } 486 | 487 | // Funzione usata per firmare la query di aggiunta oggetto in lista (una misura anti userbot) 488 | function generate_query_random_char() { 489 | return String.fromCharCode(65 + Math.floor(Math.random() * 26)) 490 | } 491 | 492 | // Questa funzione non dovrebbe essere in logic... <- TODO 493 | function craf_line_error(items_list, player_id) { 494 | let parameters = [{ player_id: player_id }, ...items_list,]; 495 | return `${error_views.str.title} ${error_views.print(error_views.str.master_craftsman.craft_line, error_views.flatted_function_paparameters(parameters))}\n${error_views.str.contact_admin} 496 | }` 497 | } 498 | 499 | 500 | 501 | 502 | 503 | // ************************** queste sono funzioni che appartengono ad items!!, non dovrebbero essere qui 504 | 505 | 506 | function sort_items_fromRarity_accessory(item1, item2, rarityOrder) { 507 | let rarity1 = item1.rarity; 508 | let rarity2 = item2.rarity; 509 | 510 | if (rarityOrder[rarity1] < rarityOrder[rarity2]) { 511 | return -1; 512 | } else if (rarityOrder[rarity1] > rarityOrder[rarity2]) { 513 | return 1; 514 | } else { 515 | // Se la rarità è la stessa, ordina per nome, volendo si può aggiungere qui sopra anche per total_quantity 516 | let quantity1 = item1.quantity ? item1.quantity : item1.total_quantity; 517 | let quantity2 = item2.quantity ? item2.quantity : item2.total_quantity; 518 | 519 | if (quantity1 < quantity2) { 520 | return 1; 521 | } else if (quantity1 > quantity2) { 522 | return -1; 523 | } else if (item1.name) { 524 | let name1 = item1.name.toLowerCase(); 525 | let name2 = item2.name.toLowerCase(); 526 | 527 | if (name1 < name2) { 528 | return -1; 529 | } else if (name1 > name2) { 530 | return 1; 531 | } else { 532 | return 0; 533 | } 534 | } else { 535 | return 0; 536 | } 537 | 538 | 539 | } 540 | } 541 | 542 | function sort_items_fromRarity(items_list) { 543 | let rarityOrder = { 544 | C: 0, 545 | NC: 1, 546 | R: 2, 547 | UR: 3, 548 | L: 4, 549 | E: 5, 550 | UE: 6, 551 | X: 7, 552 | U: 8, 553 | S: 9, 554 | }; 555 | return items_list.sort((a, b) => sort_items_fromRarity_accessory(a, b, rarityOrder)) 556 | } 557 | 558 | // I creati per una rinascita 559 | function get_craftables_forRebor(player_reborn) { 560 | return item_logics.all_craftables_forReborn((isNaN(parseInt(player_reborn)) ? 1 : parseInt(player_reborn))); 561 | } 562 | 563 | function get_prefixes_ofRarity(item_rarity, player_reborn = false) { 564 | let craftables_array = get_craftables_ofRarity(item_rarity, player_reborn); 565 | return get_craftable_array_groupedPrefixes(craftables_array); 566 | } 567 | 568 | function is_craftable_rarity(item_rarity) { 569 | return (item_logics.get_all_craftable_rarity().indexOf(item_rarity) >= 0); 570 | } 571 | 572 | function avaible_rarities(player_reborn) { 573 | return item_logics.get_all_craftable_rarity(player_reborn); 574 | } 575 | 576 | -------------------------------------------------------------------------------- /LootBot/logic/necro_descent/nd_combact.js: -------------------------------------------------------------------------------- 1 | // Il combattiento 2 | 3 | 4 | // global_beat è una funzione che in runtime viene eseguita ogni 30 secondi. 5 | // Per ogni file combattimento in ./necro_descent/Sources/Combacts aggiorna lo stato del combattimento -------------------------------------------------------------------------------- /LootBot/logic/necro_descent/nd_logic.js: -------------------------------------------------------------------------------- 1 | // La logica di necro_descent. Ottiene ed elabora dati, è il primo ponte di comunicazione tra il message_manager e le sotto-logiche (nd_combact, nd_maze, nd_player) 2 | 3 | const model = require("../../model/JSON_managers/specifiche/necro_descent"); 4 | const player_logics = require("../players"); 5 | const nd_maze = require("../../logic/necro_descent/nd_maze") 6 | const nd_player = require("../../logic/necro_descent/nd_player") 7 | 8 | 9 | module.exports = { 10 | // GIOCATORE 11 | player_infos: load_pleyer_info, 12 | player_teamID: load_player_teamId, 13 | new_nd_player: create_nd_player, 14 | get_ndplayer: get_nd_player, 15 | 16 | //LABIRINTO 17 | get_front_gates: get_front_gates, 18 | get_relative_facing: get_relative_facing, 19 | get_maze_room: get_maze_room, 20 | get_instance: get_instance_info, 21 | update_instance_info: update_instance_info, 22 | descent_controlls: descent_controlls, 23 | 24 | } 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | // ********************* INSTANCE_INFO (ALTARE) 33 | 34 | // Crea un nuovo altare del sacrificio. (se ne esiste già uno sarà sovrascritto) 35 | function init_instance_info(team_id) { // era async... 36 | let instance_info = model.altar_template(); 37 | init_maze(instance_info); 38 | return instance_info;// await model.update_instance_info(team_id, instance_info); 39 | } 40 | 41 | 42 | // Espone il contenuto di /LootBot/sources/necro_descent/MansionsAltars/altat_${team_id}.json (lo crea, se necessario) 43 | async function get_instance_info(player_info) { 44 | let instance_info = await model.get_instance_info(player_info.account_id, player_info.team_id); 45 | if (instance_info.esit == false) { 46 | if (instance_info.error == 0) { // potrebbe pur essere che non sia stato mai creato… 47 | instance_info.content = init_instance_info(player_info.team_id); 48 | // if (instance_info == false) { // niente.. proprio non si riesce ad accedervi. Male! 49 | // return ({ 50 | // esit: false, 51 | // error: 0 52 | // }); 53 | // } 54 | } else if (instance_info.error == 1) { 55 | return ({ 56 | esit: false, 57 | error: 0 58 | }); 59 | } else if (instance_info.error == 2){ 60 | return ({ 61 | esit: false, 62 | error: 1, 63 | locked_by: instance_info.locked_by 64 | }); 65 | } 66 | } 67 | 68 | return { 69 | esit: true, 70 | results: instance_info.content 71 | }; 72 | } 73 | 74 | 75 | // Aggiorna instance_info 76 | async function update_instance_info(team_id, new_instance_info) { 77 | let instance_info = await model.update_instance_info(team_id, new_instance_info); 78 | if (instance_info == false) { // potrebbe pur essere che non sia stato mai creato… 79 | return { 80 | esit: false, 81 | }; 82 | } 83 | return { 84 | esit: true, 85 | results: instance_info 86 | }; 87 | } 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | // ********************* MAZE 96 | 97 | // Genera un nuovo labirinto e lo aggiunge ad instance_info (modificandolo) 98 | // la funzione è chiamata da new_altar (prima inizializzazione) ed ogni volta che un labirinto viene distrutto… 99 | function init_maze(instance_info) { 100 | let new_maze = nd_maze.new_maze(); 101 | instance_info.current_maze.difficulty = 2* Math.floor((new_maze.maze_difficulty*2 + new_maze.maze.length)/10); 102 | instance_info.current_maze.maze = new_maze.maze; 103 | instance_info.current_maze.entry_room = new_maze.starting_room_id; 104 | } 105 | 106 | // Espone la funzione in nd_maze. 107 | function get_maze_room(maze, room_id) { 108 | return nd_maze.get_room(maze, room_id) 109 | } 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | // ********************* ND_PLAYER 118 | 119 | //Carico player_info (espone la funzione in player_logics) 120 | async function load_pleyer_info(telegram_user_id) { 121 | return await player_logics.player_full_infos(telegram_user_id) 122 | } 123 | 124 | //Carico team_id per il giocatore. (espone la funzione in player_logics) 125 | async function load_player_teamId(player_id) { 126 | return await player_logics.player_teamId(player_id); 127 | } 128 | 129 | // Espone la funzione in nd_player. 130 | function create_nd_player(player_info) { 131 | return nd_player.generate_card(player_info); 132 | } 133 | 134 | // Espone la funzione in nd_maze. 135 | function get_nd_player(maze, account_id) { 136 | return nd_maze.get_ndplayer(maze, account_id) 137 | } 138 | 139 | // Espone la funzione in nd_maze. 140 | function get_relative_facing(nd_player){ 141 | return nd_maze.relative_facing(nd_player.current_facing) 142 | } 143 | 144 | // Espone la funzione in nd_maze. 145 | function get_front_gates(room, nd_player){ 146 | return nd_maze.get_gates(room, nd_player.current_facing); 147 | } 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | // ACCESSORIE (SPECIFICHE) 156 | 157 | // Controlli su instance_info, generazione della scheda nd_player e aggiornamento di instance_info 158 | function descent_controlls(player_info, instance_info) { // era async 159 | let can_proceed = { 160 | esit: false, 161 | cause: -1, 162 | nd_player: {} 163 | }; 164 | 165 | //controllo se player_info.account_id è già tra i giocatori nell'istanza 166 | let already_in_maze = nd_maze.get_ndplayer(instance_info.current_maze, player_info.account_id) 167 | can_proceed.esit = !already_in_maze.esit; 168 | can_proceed.cause = can_proceed.esit ? -1 : 0; 169 | 170 | //if (can_proceed.esit == true){ 171 | // can_proceed.esit = controllo se player_info.account_id è tra instance_info.worthy_players_ids 172 | // can_proceed.cause = can_proceed.esit == false? 1 : -1; } 173 | 174 | 175 | 176 | //if (can_proceed.esit == true){ 177 | // can_proceed.esit = controllo se instance_info.nd_players.length +1 è ancora nel range accettabile 178 | // can_proceed.cause = can_proceed.esit == false? 2 : -1; } 179 | 180 | //if (can_proceed.esit == true){ 181 | // can_proceed.esit = controllo lo stato del giocatore in instance_info.nd_players[].states (se è morto verifico prima la data di morte...) 182 | // can_proceed.cause = can_proceed.esit == false? 3 : -1;} 183 | 184 | 185 | 186 | if (can_proceed.esit == true) { // aggiorno la persistenza di instance_info 187 | //creo la scheda nd_player per player_info 188 | can_proceed.nd_player = create_nd_player(player_info); 189 | //let starting_room = nd_maze.get_room(instance_info.current_maze.maze, instance_info.current_maze.entry_room); 190 | 191 | //Lascio cadere il giocatore nella prima stanza 192 | can_proceed.nd_player.current_room_id = instance_info.current_maze.entry_room; 193 | //starting_room.room.room_nd_players.push(player_card.account_id); // per ora do per scontato esit. Se no non ne esco.. ma non è il massimo 194 | 195 | //Imposto una direzione (casuale) verso cui sta guardando 196 | can_proceed.nd_player.current_facing = nd_maze.random_direction(); 197 | 198 | //aggiungo la scheda giocatore ad instance_info 199 | instance_info.current_maze.nd_players.push({...can_proceed.nd_player}); 200 | //can_proceed.esit = (await update_instance_info(player_info.team_id, instance_info)).esit; 201 | // can_proceed.cause = can_proceed.esit == false ? 4 : -1; 202 | } 203 | 204 | 205 | return can_proceed; 206 | 207 | } 208 | 209 | -------------------------------------------------------------------------------- /LootBot/logic/necro_descent/nd_player.js: -------------------------------------------------------------------------------- 1 | // I giocatori in nd 2 | 3 | 4 | module.exports = { 5 | generate_card: generate_playercard, 6 | generate_mob: generate_mobcard 7 | } 8 | 9 | 10 | function pg_skeletron() { 11 | return { 12 | account_id: -1, 13 | current_room_id: -1, // id della stanza nel labirinto 14 | current_facing: "", 15 | current_action_end_date: -1, // Orario fino al quale l'utente è forzato ad attendere (non può fare altro) 16 | hp: 20, // punti colpo 17 | stats: { 18 | deads: 0, // contatore del numero di morti nel labirinto corrente 19 | attack: 2, // Attacco semplice 20 | special_attack: 1, // Attacco speciale 21 | defense: 5, // difesa 22 | special_defense: 2, // difesa speciale 23 | velocity: 3, // velocità 24 | energy: 10, // energia (consumata dalle azioni. Si può recuperare con riposo (+2 in 2 minuti) o tramite alleato) 25 | healing: 1, // capacità curativa 26 | skills: [], // abilità 27 | }, 28 | states: [], // stati (di movimento, di combattimento, di morte…) 29 | last_dead_date: -1, // Orario dell'ultima morte (per respawn) 30 | equip: { // equipaggiamento 31 | weapon_id: -1, 32 | shield_id: -1, 33 | armor_id: -1, 34 | talisman_id: -1 35 | }, 36 | knapsack: [] // sacca 37 | } 38 | } 39 | 40 | function pg_skills(skill_id) { 41 | let skill_name = ""; 42 | switch (skill_id) { 43 | case(1): { 44 | skill_name = "Guarigione" // 💚 // Aumenta istantaneamente gli hp del giocatore obbiettivo (in base al suo healing e a special_attack di chi invoca). Chi la usa attende in maniera proporzionata. 45 | break; 46 | } 47 | case(2): { 48 | skill_name = "Supporto" // 🔰 // Aumenta istantaneamente velocità e/o attacco del giocatore obbiettivo in base a special_attack. Chi la usa attende in maniera proporzionata. 49 | break; 50 | } 51 | case(3): { 52 | skill_name = "Impeto di Fiamme" // 🔥 // ATTACCO SPECIALE, possibilità di obbiettivo-"Bruciato" 53 | break; 54 | } 55 | case(4): { 56 | skill_name = "Tempesta Folgorante" // ⚡️ ATTACCO SPECIALE, possibilità di obbiettivo-"Paralizzato" 57 | break; 58 | } 59 | case(5): { 60 | skill_name = "Furia dei Mari" // 🌊 ATTACCO SPECIALE, possibilità di obbiettivo-"Rallentato" 61 | break; 62 | } 63 | case(6): { 64 | skill_name = "Apprendista" // ⭐️ possibilità di apprendere un altra abilità (al posto di questa) 65 | break; 66 | } 67 | default: break; // skill_id == 0 // 👋 68 | } 69 | 70 | return ({id: skill_id, name: skill_name}); 71 | 72 | } 73 | 74 | function pg_types(vocazione){ 75 | /* 76 | 1 - Luce (che è particolarmente efficace su Ombra) 77 | 2 - Ombra (che è particolarmente efficace su elettro) 78 | 3 - elettro (che è particolarmente efficace su acqua) 79 | 4 - acqua (che è particolarmente efficace su fuoco) 80 | 5 - fuoco (che è particolarmente efficace su Luce) 81 | */ 82 | switch (vocazione) { 83 | case (2): { // ("Sciamano Elementalista"): { 84 | return 3+Math.floor(Math.random()*2); // uno qualunque ma non luce o ombra... 85 | } 86 | case (3): { //("Esploratore Druido"): { 87 | return 3; 88 | } 89 | case (4): { //("Incantaspade"): { 90 | return 5; 91 | } 92 | case (5): { //("Consacratore Divino"): { 93 | return 4; 94 | } 95 | case (6): { //("Spaccateste"): { 96 | return 2; 97 | } 98 | case (7): { //("Discepolo dei Draghi"): { 99 | return 1; 100 | } 101 | case (8): { //("Barbaro"): { 102 | return 2; 103 | } 104 | case (9): { // ("Predone"): { 105 | return 2; 106 | } 107 | default: { // Cittadino! 108 | return 1; 109 | } 110 | } 111 | } 112 | 113 | 114 | 115 | // La scheda del giocatore per nd. 116 | function generate_playercard(player_info) { 117 | // in ingresso credo 'reborn' e 'class' di player 118 | 119 | 120 | let player_basecard = pg_skeletron(); 121 | 122 | // Punti colpo 123 | switch (player_info.reborn) { 124 | case (4): { 125 | player_basecard.hp *= 2; 126 | break; 127 | } 128 | case (5): { 129 | player_basecard.hp *= 5; 130 | break; 131 | } 132 | case (6): { 133 | player_basecard.hp *= 5; 134 | break; 135 | } 136 | default: { 137 | if (player_info.reborn > 6){ 138 | player_basecard.hp *= 5; 139 | player_basecard.hp += 10*(player_info.reborn-6) 140 | } else { 141 | player_basecard.hp += 5; 142 | } 143 | break; 144 | } 145 | } 146 | 147 | // Statistiche 148 | switch (player_info.class) { 149 | case (2): { // ("Sciamano Elementalista"): { 150 | player_basecard.stats.attack += 1; 151 | player_basecard.stats.defense += 1; 152 | player_basecard.stats.special_attack += 3; 153 | player_basecard.stats.special_defense += 3; 154 | player_basecard.stats.skills.push(pg_skills(Math.floor(Math.random() * 7))) 155 | player_basecard.stats.skills.push(pg_skills(6)) 156 | 157 | break; 158 | } 159 | case (3): { //("Esploratore Druido"): { 160 | player_basecard.stats.attack += 2; 161 | player_basecard.stats.defense += 1; 162 | player_basecard.stats.special_attack += 3; 163 | player_basecard.stats.special_defense += 2; 164 | player_basecard.stats.skills.push(pg_skills(4)) 165 | player_basecard.stats.skills.push(pg_skills(6)) 166 | break; 167 | } 168 | case (4): { //("Incantaspade"): { 169 | player_basecard.stats.attack += 2; 170 | player_basecard.stats.defense += 1; 171 | player_basecard.stats.special_attack += 3; 172 | player_basecard.stats.special_defense += 2; 173 | player_basecard.stats.skills.push(pg_skills(3)) 174 | player_basecard.stats.skills.push(pg_skills(6)) 175 | break; 176 | } 177 | case (5): { //("Consacratore Divino"): { 178 | player_basecard.stats.attack += 1; 179 | player_basecard.stats.defense += 1; 180 | player_basecard.stats.special_attack += 2; 181 | player_basecard.stats.special_defense += 4; 182 | player_basecard.stats.skills.push(pg_skills(5)) 183 | player_basecard.stats.skills.push(pg_skills(6)) 184 | break; 185 | } 186 | case (6): { //("Spaccateste"): { 187 | player_basecard.stats.attack += 6; 188 | player_basecard.stats.defense += 4; 189 | player_basecard.stats.velocity += 2; 190 | player_basecard.stats.skills.push(pg_skills(0)) 191 | break; 192 | } 193 | case (7): { //("Discepolo dei Draghi"): { 194 | player_basecard.stats.attack += 3; 195 | player_basecard.stats.defense += 3; 196 | player_basecard.stats.special_attack += 1; 197 | player_basecard.stats.special_defense += 1; 198 | player_basecard.stats.velocity += 1; 199 | player_basecard.stats.skills.push(pg_skills(6)) 200 | break; 201 | } 202 | case (8): { //("Barbaro"): { 203 | player_basecard.stats.attack += 4; 204 | player_basecard.stats.defense += 2; 205 | player_basecard.stats.special_attack += 1; 206 | player_basecard.stats.special_defense += 1; 207 | player_basecard.stats.velocity += 1; 208 | player_basecard.stats.skills.push(pg_skills(6)) 209 | break; 210 | } 211 | case (9): { // ("Predone"): { 212 | player_basecard.stats.attack += 3; 213 | player_basecard.stats.defense += 1; 214 | player_basecard.stats.special_attack += 1; 215 | player_basecard.stats.special_defense += 1; 216 | player_basecard.stats.velocity += 3; 217 | player_basecard.stats.skills.push(pg_skills(6)) 218 | player_basecard.stats.skills.push(pg_skills(6)) 219 | 220 | break; 221 | } 222 | default: { // Cittadino! 223 | player_basecard.stats.attack += 1; 224 | player_basecard.stats.defense -= 2; 225 | player_basecard.stats.special_attack += 4; 226 | player_basecard.stats.special_defense -= 1; 227 | player_basecard.stats.skills.push(pg_skills(6)) 228 | player_basecard.stats.skills.push(pg_skills(6)) 229 | break; 230 | } 231 | } 232 | 233 | player_basecard.stats.nature = pg_types(player_info.class) // Attribuisco la natura 234 | player_basecard.account_id = player_info.account_id; 235 | 236 | return player_basecard; 237 | } 238 | 239 | // I mob sono calcolati in relazione alla scheda del giocatore che (per primo) apre la porta 240 | function generate_mobcard(player_card){ 241 | let mob_basecard = pg_skeletron(); 242 | 243 | // Abilità 244 | mob_basecard.stats.skills = player_card.stats.skills.slice(); 245 | if (Math.floor(Math.random()*6) > 3){ 246 | mob_basecard.stats.skills.push(pg_skills(Math.floor(Math.random() * 7))) 247 | } 248 | if (Math.floor(Math.random()*6) < 3){ 249 | mob_basecard.stats.special_attack += Math.floor(Math.random()*6) 250 | } 251 | 252 | // Personalità 253 | switch(Math.floor(Math.random()*6)){ 254 | case 0:{ // Furioso 255 | mob_basecard.stats.attack +=1; 256 | mob_basecard.stats.velocity +=3; 257 | mob_basecard.stats.defense -=2; 258 | } 259 | case 1:{ // Pazzoide 260 | mob_basecard.stats.attack -=1; 261 | mob_basecard.stats.velocity +=3; 262 | mob_basecard.stats.defense -=2; 263 | mob_basecard.stats.healing +=2; 264 | } 265 | case 2:{ // Suicida 266 | mob_basecard.stats.attack -=1; 267 | mob_basecard.stats.velocity -=1; 268 | mob_basecard.stats.defense -=2; 269 | } 270 | default:{ 271 | mob_basecard.stats.attack -=1; 272 | mob_basecard.stats.velocity +=1; 273 | } 274 | } 275 | 276 | 277 | } 278 | 279 | // I boss devono essere definiti. L'idea iniziale ne voleva 9... Forse 3 sono sufficenti. O 5… -------------------------------------------------------------------------------- /LootBot/logic/players.js: -------------------------------------------------------------------------------- 1 | // Gestisce la parte della logica dedicata agli oggetti (LootItems) 2 | const model = require("../model/DB_managers/specific/players"); 3 | 4 | 5 | module.exports = { 6 | player_full_infos: model.player_info, 7 | player_teamId: model.team_id, 8 | player_abilities: model.player_abilities, 9 | increase_player_craft_point: model.increase_player_craft_point, 10 | assault: { 11 | infos: player_assault_infos, 12 | items_needed: player_assoult_upgrade_items, 13 | }, 14 | smuggler: { 15 | current_offert: player_smuggler_current_offert, 16 | }, 17 | money: { 18 | set: set_player_money, 19 | decrease: decrease_player_money, 20 | increase: increase_player_money, 21 | } 22 | } 23 | 24 | // Se ci fossero da unire dei dati o da confrontarli o da aggiornarli altrove... 25 | // o se ci fosse da fare qualche calcolo... 26 | // Potrebbero stare qui tutte funzioni specifiche per la logica del giocatore ...ma al momento non me ne vengono, ed 27 | // è sufficente che esponga le funzioni del suo model 28 | 29 | 30 | // controlla se il giocatore ha un team in assalto, nel giorno della preparazione, in una postazione. 31 | async function player_assault_infos(player_id){ 32 | let result = { 33 | team_id: -1, 34 | assault_phase: -1, 35 | assault_place: -1, 36 | } 37 | 38 | let player_team_id = await model.team_id(player_id); 39 | if (player_team_id.esit == true){ 40 | result.team_id = player_team_id.results; 41 | let player_place = await model.assault.get_player_place(player_id); 42 | if (player_place.esit == true){ 43 | result.assault_place = player_place.results; 44 | let assault_phase = await model.assault.get_phase(result.team_id) 45 | if (assault_phase.esit == true){ 46 | result.assault_phase = assault_phase.results; 47 | } 48 | } 49 | } 50 | return result; 51 | } 52 | 53 | // gli oggetti richiesti per il potenziamento di una postazione 54 | async function player_assoult_upgrade_items(team_id, place_id){ 55 | return await model.assault.get_upgrade_items(team_id, place_id); 56 | } 57 | 58 | // il/gli oggetti richiesti dal contrabbandiere 59 | async function player_smuggler_current_offert(player_id){ 60 | return await model.smuggler.get_current_offert(player_id); 61 | } 62 | 63 | 64 | 65 | async function set_player_money(new_ammount, telegram_user_id) { 66 | if (new_ammount == 0 || isNaN(parseInt(new_ammount)) ) { 67 | return false; 68 | } 69 | let set_result = await model.update_player_money(parseInt(new_ammount), telegram_user_id, model.types.money.set); 70 | return set_result; 71 | } 72 | 73 | 74 | async function decrease_player_money(decrease_ammount, telegram_user_id) { 75 | if (decrease_ammount == 0 || isNaN(parseInt(decrease_ammount)) ) { 76 | return false; 77 | } 78 | let set_result = await model.update_player_money(parseInt(decrease_ammount), telegram_user_id, model.types.money.decrease); 79 | return set_result; 80 | } 81 | 82 | 83 | async function increase_player_money(increase_ammount, telegram_user_id) { 84 | if (increase_ammount == 0 || isNaN(parseInt(increase_ammount)) ) { 85 | return false; 86 | } 87 | let set_result = await model.update_player_money(parseInt(increase_ammount), telegram_user_id, model.types.money.increase); 88 | return set_result; 89 | } -------------------------------------------------------------------------------- /LootBot/message_managers/reply_to_manager.js: -------------------------------------------------------------------------------- 1 | // Questo modulo gestisce la risposta a messaggi del bot (quindi dalla chat privata) 2 | 3 | 4 | let handlers_register = [ // I moduli che vogliono intercettare un messaggio in risposta, dichiarano qui una lista di "triggers" (array) a cui si registrano 5 | // Gli handler sono funzioni che accettano in ingresso il messaggio originale E il messaggio in risposta come parametri separati 6 | 7 | { 8 | triggers: ["Lista commissione", "Liste craft"] , 9 | handler: require("./specific/master_craftsman").replyDispatcher 10 | }, 11 | ] 12 | 13 | 14 | // In base a titolo (prima linea del messaggio originale) cerca un handler in handlers_register e delega la risposta. 15 | // La funzione può restituire un oggetto response o false 16 | module.exports.manage = async (message) => { 17 | if (typeof message.reply_to_message.text == "string" ){ 18 | for (let tmp in handlers_register) { 19 | const { handler, triggers } = handlers_register[tmp]; 20 | const message_title = message.reply_to_message.text.split("\n")[0]; 21 | if (handler && triggers && triggers.some(trigger => message_title.toLowerCase().match(trigger.toLowerCase()) )) { 22 | // Trovato un gestore che gestisce l'input: Sto! 23 | return (await handler(message.reply_to_message, message)); 24 | } 25 | } 26 | } 27 | 28 | return(false) 29 | } -------------------------------------------------------------------------------- /LootBot/message_managers/views_util.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ita_gender_impl_singular: (string, gender) => `${string}${gender == "F" ? "a": "o"}`, 3 | ita_gender_impl_singular_all: (string, gender, symbol= "*") => `${string.split(symbol).join(gender=="F" ? "a": "o")}`, 4 | 5 | menu_strings: { // Questo oggetto dovrebbe pian piano essere riempito con tutte le stringhe che possono essere scritte al bot 6 | back_to_menu: "Torna al menu", 7 | square: { 8 | main: "Piazza 💰", 9 | master_craftsman: "Mastro Artigiano ⚒" 10 | }, 11 | team: { 12 | mansionsAltare: "Altare Sacrificale ⛩️", 13 | } 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /LootBot/model/DB_managers/db.js: -------------------------------------------------------------------------------- 1 | // Espone una funzione query per query verso il database 2 | 3 | const mysql = require('mysql'); 4 | const config = require('./specific/db_config'); 5 | const queries = require("./queries"); 6 | const error_messages = require("../../views/errors"); 7 | 8 | module.exports.queries = queries; 9 | module.exports.messaggi_errore = error_messages; 10 | 11 | module.exports = { 12 | queries: queries, 13 | error_messages: error_messages, 14 | query: query, // esegue query sulla pool 15 | closeConnection: closeConnection // chiude la connessione verso il database. Da chiamare allo spegnimento 16 | } 17 | 18 | // Connessione verso il database 19 | const pool = mysql.createPool({ 20 | connectionLimit: 10, 21 | host: config.host, 22 | user: config.dbuser, 23 | password: config.dbpassword, 24 | database: config.dbdatabase, 25 | }) 26 | 27 | // Funzione per query sul DB 28 | function query(queryString, values=[]) { 29 | return new Promise((resolve) => { 30 | try { 31 | pool.query(queryString, values, (error, results) => { 32 | if (error) { 33 | console.error(error_messages.print(error_messages.str.database.query, queryString)); 34 | console.error(error); 35 | return resolve(false); 36 | } 37 | 38 | return resolve(results); 39 | }); 40 | } catch (error) { 41 | console.error(error_messages.print(error_messages.str.database.query, queryString)); 42 | console.error(error); 43 | return resolve(false); 44 | } 45 | }); 46 | } 47 | 48 | 49 | // Chiude la connessione verso il db (da chiamare pirma della chiusura del main.js) 50 | function closeConnection() { 51 | connection.end((error) => { 52 | if (error) { 53 | console.error(error_messages.print(error_messages.str.database.chiusura)); 54 | return; 55 | } 56 | //console.log('Connessione al database chiusa con successo!'); 57 | }); 58 | } -------------------------------------------------------------------------------- /LootBot/model/DB_managers/queries.js: -------------------------------------------------------------------------------- 1 | // le query verso il database 2 | // Online dicono di non farlo, di non creare query in questo modo e di usare ? . Ma è impossibile che gli input di queste funzioni siano compromessi. O no? 3 | 4 | const tables_util = require("./specific/db_config") 5 | const tables_names = tables_util.tables; 6 | const tables_structs = tables_util.structures; 7 | 8 | module.exports = { 9 | tables_structs: tables_structs, 10 | tables_names: tables_names, 11 | players: { 12 | full_info: player_full_info, 13 | full_info_from_nickname: full_info_from_nickname, 14 | team_id: player_teamId, 15 | player_abilities: player_abilities, 16 | 17 | //Soldi 18 | set_player_money: set_player_money, 19 | increase_player_money: increase_player_money, 20 | decrease_player_money: decrease_player_money, 21 | 22 | // PC 23 | increase_player_craft_point: increase_player_craft_point 24 | }, 25 | smuggler: { 26 | current_offert: player_smuggler_current_offert 27 | }, 28 | assault: { 29 | place: player_assault_place, 30 | phase: player_assault_phase, 31 | upgrade_items: player_assault_placeUpgrade_items 32 | }, 33 | items: { 34 | allItems: allItems, 35 | needed: items_neededFor, 36 | }, 37 | inventories: { 38 | complete: complete_inventoriOf, // GET di Zaino completo 39 | item: inventory_item_infos, // item_infos di un oggetto nello zaino (quantità, durabilità…) 40 | 41 | update_items_quantity: update_items_quantity, // Su un array [player_Id, item_Id, new_quantity] 42 | 43 | player_equip: inventory_equip, // GET di Equipaggiamento completo 44 | equip_item_info: inventory_equip_item_info, // GET di oggetto {tables_structs.players.equip_item_info} per un item_id 45 | 46 | update_equip_item_durability: update_equip_item_durability // aggiorna la scadenza di un oggetto 47 | 48 | } 49 | } 50 | 51 | // **************************************************************************** SELECT 52 | 53 | // PLAYERS 54 | function player_full_info (telegram_user_id) { 55 | return `SELECT ${tables_structs.printObject(tables_structs.players.main_info)} FROM ${tables_names.players} WHERE ${tables_structs.players.telegram_id} = ${telegram_user_id}`; 56 | } 57 | 58 | function full_info_from_nickname (telegram_nickname) { 59 | return `SELECT ${tables_structs.printObject(tables_structs.players.main_info)} FROM ${tables_names.players} WHERE LOWER(${tables_structs.players.main_info.nickname}) = LOWER("${telegram_nickname}")`; 60 | } 61 | 62 | function player_teamId (player_id) { 63 | return `SELECT ${tables_structs.teams.team_id} FROM ${tables_names.team_player} WHERE ${tables_structs.teams.player_id} = ${player_id}`; 64 | } 65 | 66 | 67 | function player_abilities(player_id){ 68 | return `SELECT ${tables_names.ability}.${tables_structs.ability.ability_id}, ${tables_names.ability}.${tables_structs.ability.ability_level}, ${tables_names.ability_list}.${tables_structs.abiliti_list.ability_power} AS ability_power FROM ${tables_names.ability}, ${tables_names.ability_list} WHERE ${tables_structs.ability.player_id} = ${player_id} `; 69 | } 70 | 71 | function player_assault_place (player_id) { 72 | return `SELECT ${tables_structs.assault.places} FROM ${tables_names.assault_places} WHERE ${tables_structs.teams.player_id} = ${player_id}`; 73 | } 74 | 75 | function player_assault_phase (team_id) { 76 | return `SELECT ${tables_structs.assault.phase} FROM ${tables_names.assault} WHERE ${tables_structs.teams.team_id} = ${team_id}`; 77 | } 78 | 79 | function player_assault_placeUpgrade_items (team_id, place_id) { 80 | return `SELECT ${tables_structs.assault.item_id}, ${tables_structs.assault.item_quantity} FROM ${tables_names.assault_items} WHERE ${tables_structs.teams.team_id} = ${team_id} AND ${tables_structs.assault.places} = ${place_id}`; 81 | } 82 | 83 | function player_smuggler_current_offert (player_id) { 84 | return `SELECT ${tables_structs.smuggler.item_id} FROM ${tables_names.smuggler_offerts} WHERE ${tables_structs.teams.player_id} = ${player_id}`; 85 | } 86 | 87 | function inventory_equip(player_id){ 88 | return `SELECT ${tables_structs.printObject(tables_structs.players.equip_info)} FROM ${tables_names.players} WHERE ${tables_structs.players.key} = ${player_id}`; 89 | } 90 | 91 | function inventory_equip_item_info(item_id){ 92 | // SELECT weapon_id, weapon2_id, weapon3_id FROM player WHERE id = 93 | return `SELECT ${tables_structs.printObject(tables_structs.players.equip_item_info)} FROM ${tables_names.items} WHERE ${tables_structs.items.key} = ${item_id}`; 94 | } 95 | 96 | // ITEMS 97 | function allItems() { 98 | return `SELECT * FROM ${tables_names.items}` 99 | } 100 | 101 | function items_neededFor (item_id) { 102 | return `SELECT ${tables_structs.printObject(tables_structs.craft.needed)} FROM ${tables_names.craft} WHERE ${tables_structs.craft.key} = ${item_id}`; 103 | } 104 | 105 | // INVENTORY 106 | function inventory_item_infos(item_id, player_id) { 107 | return `SELECT * FROM ${tables_names.inventory} WHERE ${tables_structs.inventory.item_id} = ${item_id} AND ${tables_structs.inventory.player_id} = ${player_id}`; 108 | } 109 | 110 | function complete_inventoriOf(player_id){ 111 | return `SELECT * FROM ${tables_names.inventory} WHERE ${tables_structs.inventory.player_id} = ${player_id}`; 112 | } 113 | 114 | // **************************************************************************** UPDATE 115 | 116 | // PLAYERS 117 | function increase_player_money(increase_ammount, telegram_user_id){ 118 | let query = `UPDATE ${tables_names.players} SET ${tables_structs.players.main_info.money} = ${increase_ammount}`; 119 | query += ` WHERE ${tables_structs.players.main_info.account_id} = ${telegram_user_id}`; 120 | 121 | return query; 122 | } 123 | 124 | function decrease_player_money(decrease_ammount, telegram_user_id){ 125 | let query = `UPDATE ${tables_names.players} SET ${tables_structs.players.main_info.money} =`; 126 | query += ` GREATEST( ${tables_structs.players.main_info.money} - ${decrease_ammount}, 0)`; 127 | query += ` WHERE ${tables_structs.players.main_info.account_id} = ${telegram_user_id}`; 128 | 129 | return query; 130 | } 131 | 132 | 133 | function set_player_money(new_ammount, telegram_user_id){ 134 | let query = `UPDATE ${tables_names.players} SET ${tables_structs.players.main_info.money} = ${new_ammount}`; 135 | query += ` WHERE ${tables_structs.players.main_info.account_id} = ${telegram_user_id}`; 136 | 137 | return query; 138 | } 139 | 140 | 141 | function increase_player_craft_point(increase_ammount, telegram_user_id){ 142 | let query = `UPDATE ${tables_names.players} SET`; 143 | query += ` ${tables_structs.players.craft.craft_point} = ${tables_structs.players.craft.craft_point} + ${increase_ammount},`; 144 | query += ` ${tables_structs.players.craft.daily_pint} = ${tables_structs.players.craft.daily_pint} + ${increase_ammount},`; 145 | query += ` ${tables_structs.players.craft.weekly_point} = ${tables_structs.players.craft.weekly_point} + ${increase_ammount}`; 146 | query += ` WHERE ${tables_structs.players.main_info.account_id} = ${telegram_user_id}`; 147 | 148 | return query; 149 | } 150 | 151 | // INVENTORY 152 | function reduce_item_quantity(updates_array){ // in ingresso, updates_array è un array di [playerId, itemId, reduce_quantity] 153 | let query = `INSERT INTO ${tables_names.inventory} (${tables_structs.inventory.player_id}, ${tables_structs.inventory.item_id}, ${tables_structs.inventory.quantity}) `; 154 | query += `VALUES ? ON DUPLICATE KEY UPDATE ${tables_structs.inventory.quantity} = GREATEST(${tables_structs.inventory.quantity} - VALUES(${tables_structs.inventory.quantity}), 0)` 155 | return query; 156 | } 157 | 158 | function increment_item_quantity(updates_array){ // in ingresso, updates_array è un array di [player_Id, item_Id, reduce_quantity] 159 | let query = `INSERT INTO ${tables_names.inventory} (${tables_structs.inventory.player_id}, ${tables_structs.inventory.item_id}, ${tables_structs.inventory.quantity}) `; 160 | query += `VALUES ? ON DUPLICATE KEY UPDATE ${tables_structs.inventory.quantity} = ${tables_structs.inventory.quantity} + VALUES(${tables_structs.inventory.quantity})` 161 | return query; 162 | } 163 | 164 | function update_items_quantity(){ // in ingresso, updates_array è un array di [player_Id, item_Id, new_quantity] 165 | let query = `INSERT INTO ${tables_names.inventory} (${tables_structs.inventory.player_id}, ${tables_structs.inventory.item_id}, ${tables_structs.inventory.quantity}) `; 166 | query += `VALUES ? ON DUPLICATE KEY UPDATE ${tables_structs.inventory.quantity} = VALUES(${tables_structs.inventory.quantity})` 167 | return query; 168 | } 169 | 170 | function update_equip_item_durability(item_id, player_id, new_durability, max_durability= -1){ 171 | let query = `UPDATE ${tables_names.inventory} SET ${tables_structs.inventory.durability} = ${new_durability}` 172 | if(max_durability != -1){ 173 | query += `, ${tables_structs.inventory.max_durability} = ${max_durability}` 174 | } 175 | query += `WHERE ${tables_structs.inventory.player_id} = ${player_id} AND ${tables_structs.inventory.item_id} = ${item_id}` 176 | return query 177 | } 178 | -------------------------------------------------------------------------------- /LootBot/model/DB_managers/specific/db_config.js: -------------------------------------------------------------------------------- 1 | const config = require("../../../utility/config"); 2 | const utility = require("../../../utility/utils"); // per poter esporre structures 3 | 4 | const db_config = {}; 5 | 6 | db_config.dbuser = config.database.main_user // utente database 7 | db_config.dbpassword = config.database.main_user_password // password database 8 | db_config.dbdatabase = config.database.main_database 9 | db_config.host = config.database.main_host; 10 | 11 | db_config.tables = { 12 | inventory: "inventory", 13 | items: "item", 14 | craft: "craft", 15 | players: "player", 16 | team_player: "team_player", 17 | ability: "ability", 18 | ability_list: "ability_list", 19 | 20 | assault: "assault", 21 | smuggler_offerts: "merchant_offer", 22 | assault_places: "assault_place_player_id", 23 | assault_items: "assault_place_item", 24 | } 25 | 26 | db_config.structures = utility.db_structures; // Per comodità 27 | 28 | module.exports = db_config; -------------------------------------------------------------------------------- /LootBot/model/DB_managers/specific/inventory.js: -------------------------------------------------------------------------------- 1 | const db = require("../db"); 2 | 3 | module.exports = { 4 | complete: get_complete_inventoryOf, 5 | items: { 6 | update_quantities_Of: update_quantities_Of, 7 | }, 8 | equip: { 9 | complete: get_equipOf, // Equipaggiamento completo {sword, armor, shield} 10 | item_infos: get_itemInfos_fromInventoryOf, // quantità, durabilità e durabilità massima di un oggetto nello zaino 11 | //equipitem_info: get_equipInfo_ofItem, // questa funzione è sostituita da get_itemPowerInfo() del controller inventory (che esegue la ricerca sull'array in ram) 12 | equipitem_updateDurability: update_equipItem_durability 13 | } 14 | } 15 | 16 | // GETS 17 | 18 | // Lo zaino completo per player_lootId 19 | async function get_complete_inventoryOf(player_lootId) { 20 | return new Promise(async function (complete_inventory) { 21 | let results = await db.query(db.queries.inventories.complete(player_lootId)); 22 | if (results === false) { 23 | return complete_inventory({ 24 | esit: false, 25 | message_text: db.error_messages.print(db.error_messages.str.inventory.load_fullInventory, player_lootId) 26 | }); 27 | } 28 | 29 | return complete_inventory({ 30 | esit: true, 31 | player_inventory: results 32 | }); // 33 | }); 34 | } 35 | 36 | async function update_quantities_Of(update_array) { // [player_Id, item_Id, new_quantity] 37 | return new Promise(async function (quantities_update) { 38 | let results = await db.query(db.queries.inventories.update_items_quantity(), update_array); 39 | if (results === false) { 40 | return quantities_update({ 41 | esit: false, 42 | message_text: db.error_messages.print(db.error_messages.str.inventory.update_items, update_array) 43 | }); 44 | } 45 | 46 | return quantities_update({ 47 | esit: true, 48 | update_in: results.affectedRows ? results.affectedRows : 0 49 | }); // 50 | }); 51 | } 52 | 53 | // SETS 54 | 55 | //************************************************************************************************************* 56 | // Qelle che seguono sono funzioni di test, un tentativo di porting attualmente in pausa… 57 | 58 | // quantity durability, max_durability di un oggetto nello zaino di player_lootId 59 | async function get_itemInfos_fromInventoryOf(player_lootId, item_id) { 60 | return new Promise(async function (complete_inventory) { 61 | let results = await db.query(db.queries.inventories.item(item_id, player_lootId)); 62 | if (results == false) { 63 | console.error(db.error_messages.print(db.error_messages.str.inventory.load_fullInventory, player_lootId)); 64 | return complete_inventory(false); 65 | } 66 | 67 | return complete_inventory({ 68 | quantity: results[0][db.queries.tables_structs.inventory.quantity], 69 | durability: results[0][db.queries.tables_structs.inventory.durability], 70 | max_durability: results[0][db.queries.tables_structs.inventory.max_durability], 71 | }); // 72 | }); 73 | } 74 | 75 | // L'equipaggiamento di player_lootId (restituisce {sword, armor, shield}) 76 | async function get_equipOf(player_lootId) { 77 | return new Promise(async function (equip_res) { 78 | 79 | 80 | let results = await db.query(db.queries.inventories.player_equip(player_lootId)); 81 | if (results == false || results.len) { 82 | console.error(db.error_messages.print(db.error_messages.str.inventory.load_equip, player_lootId)); 83 | return equip_res(false); 84 | } 85 | 86 | response.esit = true; 87 | response.equip = { 88 | sword: results[0][db.queries.tables_structs.players.equip_info.sword], 89 | armor: results[0][db.queries.tables_structs.players.equip_info.armor], 90 | shield: results[0][db.queries.tables_structs.players.equip_info.shield], 91 | } 92 | 93 | return equip_res(results[0]); // 94 | }); 95 | } 96 | 97 | // {sword_power, power_armor, power_shield, rarity} di un ogetto equipaggiabile 98 | async function get_equipInfo_ofItem(item_id) { 99 | return new Promise(async function (equipItem_info_res) { 100 | let results = await db.query(db.queries.inventories.equip_item_info(item_id)); 101 | if (results == false) { 102 | console.error(db.error_messages.print(db.error_messages.str.items.load_equipInfo, item_id)); 103 | return equipItem_info_res(false); 104 | } 105 | 106 | return equipItem_info_res({ 107 | sword_power: results[0][db.queries.tables_structs.players.equip_item_info.sword_power], 108 | armor_power: results[0][db.queries.tables_structs.players.equip_item_info.shield_power], 109 | shield_power: results[0][db.queries.tables_structs.players.equip_item_info.shield_power], 110 | }); // 111 | }); 112 | } 113 | 114 | // UPDATES 115 | 116 | async function update_equipItem_durability(item_id, player_lootId, durability, max_durability = -1) { 117 | return new Promise(async function (update_durability_res) { 118 | let results = await db.query(db.queries.inventories.update_equip_item_durability(item_id, player_lootId, durability, max_durability)); 119 | if (results == false) { 120 | console.error(db.error_messages.print(db.error_messages.str.inventory.load_equip, player_lootId)); 121 | return update_durability_res(false); 122 | } 123 | return update_durability_res(true); // 124 | }); 125 | } 126 | -------------------------------------------------------------------------------- /LootBot/model/DB_managers/specific/items.js: -------------------------------------------------------------------------------- 1 | const db = require("../db.js"); 2 | 3 | module.exports = { 4 | loadAllItems: loadAllItems, 5 | load_neededOf: load_neededOf, 6 | } 7 | 8 | async function loadAllItems() { 9 | 10 | return new Promise(async function (loadAllItems_res) { 11 | 12 | let results = await db.query(db.queries.items.allItems()); 13 | if (results == false) { 14 | console.error(db.error_messages.print(db.error_messages.str.items.load_allItems)); 15 | return loadAllItems_res(false); 16 | } 17 | return loadAllItems_res(results); 18 | }); 19 | } 20 | 21 | // carica gli oggetti necessari per un creato 22 | async function load_neededOf(id_oggetto) { 23 | return new Promise(async function (needed_res) { 24 | let results = await db.query(db.queries.items.needed(id_oggetto)); 25 | if (results == false) { 26 | console.error(db.error_messages.print(db.error_messages.str.items.load_needed), id_oggetto); 27 | return needed_res(false); 28 | } 29 | return needed_res(results[0]); 30 | 31 | }); 32 | } -------------------------------------------------------------------------------- /LootBot/model/DB_managers/specific/players.js: -------------------------------------------------------------------------------- 1 | const db = require("../db"); 2 | const utils = require("../../../utility/utils"); 3 | const query_types = { 4 | money: { 5 | set: "SET", 6 | increase: "INCREASE", 7 | decrease: "DECREASE" 8 | } 9 | } 10 | 11 | 12 | 13 | module.exports = { 14 | player_info: load_player_info, 15 | team_id: get_player_team_id, 16 | player_abilities: player_abilities, 17 | update_player_money: update_player_money, 18 | increase_player_craft_point: increase_player_craft_point, 19 | 20 | assault: { 21 | get_player_place: get_player_assault_place, 22 | get_phase: get_player_assault_phase, 23 | get_upgrade_items: get_player_assault_upgrade_items 24 | }, 25 | smuggler: { 26 | get_current_offert: get_current_offert 27 | }, 28 | 29 | 30 | types: query_types 31 | } 32 | 33 | 34 | 35 | 36 | 37 | // Scheda giocatore 38 | async function load_player_info(telegram_id) { 39 | return new Promise(async function (player_main_info_res) { 40 | if (typeof telegram_id != "string" && isNaN(telegram_id)){ 41 | return player_main_info_res({ 42 | esit: false, 43 | error_text: db.error_messages.print(db.error_messages.str.players.bad_input, telegram_id) 44 | }); 45 | } 46 | 47 | let parsed_input = isNaN(telegram_id) ? telegram_id.trim() : parseInt(telegram_id); 48 | let player_full_infos; 49 | 50 | if (typeof parsed_input == "string"){ 51 | player_full_infos = await db.query(db.queries.players.full_info_from_nickname(parsed_input)); 52 | } else { 53 | player_full_infos = await db.query(db.queries.players.full_info(parsed_input)); 54 | } 55 | 56 | if (player_full_infos == false || 57 | utils.isNully(player_full_infos) || 58 | !Array.isArray(player_full_infos) || 59 | player_full_infos.length < 1) { 60 | return player_main_info_res({ 61 | esit: false, 62 | error_text: db.error_messages.print(db.error_messages.str.players.load_mainInfo, telegram_id) 63 | }); 64 | } 65 | return player_main_info_res({ 66 | esit: true, 67 | results: player_full_infos[0] 68 | }); 69 | }); 70 | } 71 | 72 | // Id del team giocatore 73 | async function get_player_team_id(player_id) { 74 | return new Promise(async function (player_main_info_res) { 75 | let player_team_id = await db.query(db.queries.players.team_id(player_id)); 76 | 77 | if (player_team_id == false || 78 | utils.isNully(player_team_id) || 79 | !Array.isArray(player_team_id) || 80 | player_team_id.length <= 0) { 81 | return player_main_info_res({ 82 | esit: false, 83 | error_text: db.error_messages.print(db.error_messages.str.players.load_teamId, `${player_id}`) 84 | }); 85 | } 86 | return player_main_info_res({ 87 | esit: true, 88 | results: player_team_id[0].team_id 89 | }); 90 | }); 91 | } 92 | 93 | // Talenti del giocatore 94 | async function player_abilities(player_id){ 95 | let player_abilities = await db.query(db.queries.players.player_abilities(player_id)); 96 | 97 | if (player_abilities == false || 98 | utils.isNully(player_abilities) || 99 | !Array.isArray(player_abilities) || 100 | player_abilities.length <= 0) { 101 | return ({ 102 | esit: false, 103 | error_text: db.error_messages.print(db.error_messages.str.players.load_abilities, `${player_id}`) 104 | }); 105 | } 106 | return ({ 107 | esit: true, 108 | results: player_abilities 109 | }); 110 | } 111 | 112 | // Postazione in assalto per un giocatore 113 | async function get_player_assault_place(player_id) { 114 | return new Promise(async function (player_main_info_res) { 115 | let player_assault_place = await db.query(db.queries.assault.place(player_id)); 116 | 117 | if (player_assault_place == false || 118 | utils.isNully(player_assault_place) || 119 | !Array.isArray(player_assault_place) || 120 | player_assault_place.length <= 0) { 121 | return player_main_info_res({ 122 | esit: false, 123 | }); 124 | } 125 | return player_main_info_res({ 126 | esit: true, 127 | results: player_assault_place[0].place_id 128 | }); 129 | }); 130 | } 131 | 132 | // Fase corrente in assalto 133 | async function get_player_assault_phase(team_id) { 134 | return new Promise(async function (player_main_info_res) { 135 | let player_assault_phase = await db.query(db.queries.assault.phase(team_id)); 136 | 137 | if (player_assault_phase == false || 138 | utils.isNully(player_assault_phase) || 139 | !Array.isArray(player_assault_phase) || 140 | player_assault_phase.length <= 0) { 141 | return player_main_info_res({ 142 | esit: false, 143 | }); 144 | } 145 | return player_main_info_res({ 146 | esit: true, 147 | results: player_assault_phase[0].phase 148 | }); 149 | }); 150 | } 151 | 152 | // Oggetti richiesti per potenziamento di una postazione 153 | async function get_player_assault_upgrade_items(team_id, place_id) { 154 | return new Promise(async function (player_main_info_res) { 155 | let player_assault_upgrade_items = await db.query(db.queries.assault.upgrade_items(team_id, place_id)); 156 | 157 | if (player_assault_upgrade_items == false || 158 | utils.isNully(player_assault_upgrade_items) || 159 | !Array.isArray(player_assault_upgrade_items) 160 | ){ 161 | return player_main_info_res({ 162 | esit: false, 163 | results: [] 164 | }); 165 | } 166 | return player_main_info_res({ 167 | esit: true, 168 | results: player_assault_upgrade_items 169 | }); 170 | }); 171 | } 172 | 173 | // oggetto/i richiesti dal contrabbandiere 174 | async function get_current_offert(player_id){ 175 | let player_current_offert = await db.query(db.queries.smuggler.current_offert(player_id)); 176 | 177 | if (player_current_offert == false || 178 | utils.isNully(player_current_offert) || 179 | !Array.isArray(player_current_offert) 180 | ){ 181 | return ({ 182 | esit: false, 183 | results: [] 184 | }); 185 | } 186 | return ({ 187 | esit: true, 188 | results: player_current_offert 189 | }); 190 | } 191 | 192 | async function update_player_money(ammount, telegram_user_id, type) { 193 | return new Promise(async function (money_update_res) { 194 | 195 | // controllo che 196 | let query_function; 197 | switch (type) { 198 | case query_types.money.set: { 199 | query_function = db.queries.players.set_player_money 200 | break 201 | } 202 | case query_types.money.increase: { 203 | query_function = db.queries.players.increase_player_money 204 | break 205 | } 206 | case query_types.money.decrease: { 207 | query_function = db.queries.players.decrease_player_money 208 | break 209 | } 210 | } 211 | if (query_function) { 212 | let money_update = await db.query(query_function(ammount, telegram_user_id)); 213 | 214 | if (!money_update) { 215 | return money_update_res({ 216 | esit: false, 217 | }); 218 | } 219 | return money_update_res({ 220 | esit: true, 221 | results: money_update 222 | }); 223 | 224 | 225 | } 226 | 227 | return { 228 | esit: false 229 | } 230 | }); 231 | } 232 | 233 | async function increase_player_craft_point(increase_ammount, telegram_user_id) { 234 | return new Promise(async function (increase_craft_point_res) { 235 | let increase_res = await db.query(db.queries.players.increase_player_craft_point(increase_ammount, telegram_user_id)); 236 | 237 | if (!increase_res) { 238 | return increase_craft_point_res({ 239 | esit: false, 240 | }); 241 | } 242 | return increase_craft_point_res({ 243 | esit: true, 244 | results: increase_res 245 | }); 246 | }) 247 | } 248 | -------------------------------------------------------------------------------- /LootBot/model/JSON_managers/json_model.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | const appMainPath = path.dirname(require.main.filename); 5 | 6 | module.exports = { 7 | read: readJSONFile, 8 | readAndLockJSON: readAndLockJSON, 9 | write: writeJSONFile, 10 | check: fileExists 11 | } 12 | 13 | // Funzione per controllare se un file esiste 14 | async function fileExists(directoryPath) { 15 | try { 16 | await fs.promises.access(directoryPath, fs.constants.F_OK); 17 | return true; 18 | } catch (error) { 19 | // console.error(error); 20 | return false; 21 | } 22 | } 23 | 24 | // Funzione ASINCRONA per leggere il contenuto di un file JSON 25 | async function readJSONFile(filePath) { 26 | try { // Non faccio controlli sull'esistenza ne di directoryPath ne del file. Il caso è gestito individualmente dalle specifiche 27 | const data = await fs.promises.readFile(filePath); 28 | return JSON.parse(data); 29 | } catch (error) { 30 | // console.error(`Errore durante la lettura del file JSON "${filePath}": ${error}`); 31 | return false; 32 | } 33 | } 34 | 35 | // Funzione ASINCRONA per scrivere il contenuto in un file JSON 36 | async function writeJSONFile(directoryPath, filePath, updated_data) { 37 | try { 38 | if (!(await fileExists(directoryPath))) { // Nel caso in cui la cartella utente non esista, la creo 39 | // console.log("creo "+directoryPath); 40 | await fs.promises.mkdir(directoryPath, { recursive: true }); 41 | } else { 42 | // console.log("la directory esiste..."); 43 | } 44 | 45 | const jsonData = JSON.stringify(updated_data, null, 2); 46 | await fs.promises.writeFile(filePath, jsonData, 'utf8'); 47 | return updated_data; 48 | } catch (error) { 49 | //console.error(`Errore durante la scrittura del file JSON "${filePath}": ${error}`); 50 | return false; 51 | } 52 | } 53 | 54 | 55 | // Funzione per la lettura di un file condiviso 56 | // Leggo (async) e controllo subito se jsonContent.locked_by 57 | // Se è libero posso restituire il contenuto, altrimenti c'è bisogno che si aspetti. 58 | // È una soluzione semplicissima. Ci sono arrivato solo dopo 64 minuti di deliri! 59 | // Nota del giorno dopo: un implicazione è che la release del lock andrà fatta ad ogni chiamata (perche ad ogni chiamata viene letto il file) 60 | async function readAndLockJSON(filePath, user_id) { 61 | try { 62 | console.log(filePath); 63 | const jsonContent = await readJSONFile(filePath); 64 | if (jsonContent == false) { 65 | return { esit: false, error: 0 } 66 | } 67 | 68 | if (!jsonContent.locked_by || (jsonContent.lock_date + 2000) <= Date.now()) { 69 | // Nessuno ha bloccato il file, procedi con il blocco e la lettura 70 | jsonContent.locked_by = user_id; 71 | jsonContent.lock_date = Date.now(); 72 | 73 | // Scrivi il file con l'attributo locked_by 74 | fs.writeFileSync(filePath, JSON.stringify(jsonContent, null, 2), 'utf8'); 75 | 76 | // Restituisci il contenuto del file e l'ID dell'utente che lo ha bloccato 77 | return { esit: true, content: jsonContent, locked_by: user_id }; 78 | } else { 79 | // Il file è già bloccato da un altro utente 80 | return { esit: false, error: 2, locked_by: jsonContent.locked_by }; 81 | } 82 | } catch (error) { 83 | console.error(`1B. Errore durante la scrittura del file JSON "${filePath}": ${error}`); 84 | return { esit: false, error: 1};; 85 | } 86 | 87 | } -------------------------------------------------------------------------------- /LootBot/model/JSON_managers/specifiche/master_craftsman.js: -------------------------------------------------------------------------------- 1 | // Questo modulo gestisce la lettura e la scrittura del file craftsman_info.json 2 | // Per ogni utente esiste una cartella (nominata secondo il suo telegram_user_id). (questo perche possa ospitare anche altre cose, oltre a craftsman_info.json (idealmente si potrebbe mirare qui gran parte della persistenza, lasciando sul database solo quello che ha senso mettere in relazione)) 3 | 4 | const json_model = require("../json_model"); 5 | const path = require('path'); 6 | 7 | const craftsman_file_name = "craftsman_info.json"; 8 | const players_dir = path.join(path.dirname(require.main.filename), '/LootBot/sources/players/'); 9 | 10 | 11 | module.exports = { 12 | has_list: has_list, 13 | get_craftsman_info: get_craftsman_info, 14 | update_craftsman_info: update_craftsman_info, 15 | template: { 16 | craftman_info: { 17 | query_random_char: "", // Usato per limitare l'uso di user_bot (rendendo le query meno prevedibile) 18 | preserve_crafted: true, 19 | is_new: true, 20 | censure_view: false, 21 | current_rarity: "NC", 22 | current_prefix: "", 23 | current_message_id: -1, 24 | ban_until: -1, // se diverso da -1 conserva il Date.time() fino a quando il Mastro Artigiano non sarà disponibile 25 | 26 | stats: { 27 | total_loops: 0, 28 | total_pc: 0, 29 | total_craft_cost: 0, 30 | total_crafted_items: 0, 31 | total_base_items_used: 0 32 | 33 | }, 34 | 35 | items_list: [], // Array di (item_id, quantity) 36 | controll: { 37 | target_items_list: [], 38 | loops: 0, 39 | controll_date: -1, 40 | craft_point: 0, 41 | craft_cost: 0, 42 | missing_baseItems: [], 43 | used_items: { 44 | base: [], 45 | crafted: [] 46 | }, 47 | skipped: [], 48 | } 49 | } 50 | }, 51 | new_query_random_char: () => String.fromCharCode(65 + Math.floor(Math.random() * 26)) 52 | }; 53 | 54 | // Funzione per controllare se un file esiste 55 | async function has_list(telegram_user_id) { 56 | const filePath = path.join(path.join(players_dir, telegram_user_id.toString()), craftsman_file_name); 57 | 58 | return await json_model.check(filePath); 59 | } 60 | 61 | // Funzione per leggere il contenuto di un file JSON 62 | async function get_craftsman_info(telegram_user_id) { 63 | const filePath = path.join(path.join(players_dir, telegram_user_id.toString()), craftsman_file_name); 64 | 65 | return await json_model.read(filePath); // restituisce false o craftsman_info 66 | } 67 | 68 | // Funzione per scrivere il contenuto in un file JSON 69 | async function update_craftsman_info(telegram_user_id, new_craftsman_info) { 70 | let directory_path = path.join(players_dir, telegram_user_id.toString()); 71 | const filePath = path.join(directory_path, craftsman_file_name); 72 | 73 | return await json_model.write(directory_path, filePath, new_craftsman_info); 74 | } 75 | 76 | -------------------------------------------------------------------------------- /LootBot/model/JSON_managers/specifiche/necro_descent.js: -------------------------------------------------------------------------------- 1 | // Le informazioni sui combattimenti sono conservate in LootBot/sources/necro_descent/Combacts 2 | // Le informazioni sulle istanze (altari e dedali) sono conservate in LootBot/sources/necro_descent/MansionsAltars 3 | 4 | /* 5 | 6 | 7 | */ 8 | 9 | const json_model = require("../json_model"); 10 | const path = require('path'); 11 | const altar_dir = path.join(path.dirname(require.main.filename), '/LootBot/sources/necro_descent/MansionsAltars/'); 12 | const instance_file = (team_id) => { return "altar-$.json".replace('$', `${team_id}`) }; 13 | 14 | module.exports = { 15 | has_altar: has_altar, 16 | get_instance_info: get_instance_info, 17 | update_instance_info: update_instance_info, 18 | altar_template: () => { 19 | return { 20 | stats: { 21 | total_attempts: 0, // conteggio dei tentativi (uno ogni nuova generazione del labirinto) 22 | total_sacrifices: 0, // in numero di oggetti 23 | total_sacrifices_value: 0, // in valore per oggetti sacrificati 24 | }, 25 | current_altar: { // Se sacrifices_required_value - sacrifices_value > 0 -> bottone per eseguire sacrifici, altrimenti bottone per accedere al dedalo… 26 | sacrifices_required_value: Math.floor(Math.random() * 500) + 500, 27 | sacrifices_value: 0, 28 | unworthy_sacrifices: 0, // I sacrifici di oggetti di rarità <= R 29 | worthy_players_ids: [], // solo i giocatori che hanno fatto almeno un sacrificio possono accedere al dedalo… 30 | }, 31 | current_maze: { 32 | creation_date: Date.now(), // Data di creazione 33 | gate_opening_date: -1, // Momento di apertura del passaggio (su cui è calcolato il crollo) 34 | entry_room: -1, 35 | deads: 0, // contatore delle morti (mob+boss+nd_players) 36 | final_chest: [], // Tutti gli oggetti che possono essere trovati nell'ultima stanza (dopo il boss) 37 | nd_players: [], // Lista dei giocatori (oggetto nd_player) 38 | maze: [], // Mappa del labirinto 39 | } 40 | } 41 | } 42 | }; 43 | 44 | 45 | // Funzione per controllare se un file esiste 46 | async function has_altar(team_id) { 47 | const filePath = path.join(altar_dir, instance_file(team_id)); 48 | 49 | return await json_model.check(filePath); 50 | } 51 | 52 | // Funzione per leggere il contenuto di un file JSON 53 | async function get_instance_info(account_id, team_id) { 54 | const filePath = path.join(altar_dir, instance_file(team_id)); 55 | 56 | return await json_model.readAndLockJSON(filePath, account_id); // restituisce false o instance_info 57 | } 58 | 59 | // Funzione per scrivere il contenuto in un file JSON 60 | async function update_instance_info(team_id, new_instance_info) { 61 | const filePath = path.join(altar_dir, instance_file(team_id)); 62 | 63 | //Libero l'istanza 64 | delete new_instance_info.locked_by; 65 | delete new_instance_info.lock_date; 66 | 67 | return await json_model.write(altar_dir, filePath, new_instance_info); 68 | } 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /LootBot/sources/necro_descent/idea.rtf: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | 4 | 5 | 6 | 7 | __________________ 8 | 9 | IDEA INIZIALE: 10 | 11 | Fino a 5 componenti di un team possono sacrificare, una volta al mese, un E/UE/S (specifico) + una certa quantità di edollari a testa per tentare la discesa nei 10 gironi Infernali. 12 | 13 | L'istanza, generata casualmente ad inizio, è simile ai Dungeon: 14 | • Dura 6 ore e 6 minuti 15 | • È composta da 9 stanze con 3 direzioni da prendere in ognuna, piu la possibilità di tornare a quella precedente 16 | • In ogni stanza è presente un bonus o malus tattico (1) o istantaneo (2) 17 | • Il passaggio alla stanza successiva avvine 6 minuti dopo che l'ultimo componente ha scelto la sua direzione 18 | • 9 super-boss che combinano i tre elementi - fuoco, elettro, acqua - con i tipi drago. 19 | 20 | La ricompensa finale avviene al raggiungimento della Camera di Lucifero, questa: 21 | • È sbloccabile solo tramite tre campanelli, uno per tipo - dispersi casualmente nei 9 gironi (🛎) 22 | • Presenta un ultimo Super Boss: Lucifero 23 | 24 | Sconfitto il maligno, si avranno bonus in U, Pietre Drago, Mana e/o edollari: 25 | • In base al percorso coperto durante la discesa dall'intero sub-team 26 | • Concedendo ulteriori bonus nel caso che tutti i 9 super boss siano stati sconfitti 27 | 28 | 29 | (1) - Inidicazioni sull'istanza, Consumabili, Edollari, swich delle direzioni in 3 stanze, retrocessione/avanzamento forzato di stanza 30 | 31 | (2) - Piccole quantità di Mana, Super-Boss(3), danni in hp (Giocatore/Drago) e stat (drago), cetera. 32 | 33 | (3) Sono Boss il cui equipaggiamento è calcolato in relazione a 2 componenti del sub team scelti casualmente e gli hp il risultato della somma degli altri tre. Si accompagnano eventualmente ad un drago, antitesi di uno dei draghi del sub team. 34 | 35 | 🛎 36 | Tre tipi con diverse probabilità di spawn. Possono essere in qualunque tipo di stanza e il loro suono viene avvertito nella stanza precedente in base al tipo di Talismano (X) equipaggiato. 37 | 38 | __________________ 39 | 40 | IDEA IN PROGRESSO: 41 | 42 | 43 | 0.a: Iscrizione 44 | Avviene tramite "sacrifici" di oggetti fatti all'altare della magione: 45 | ogni giocatore di un team può sacrificare al massimo un oggetto al giorno, 46 | in base alla rarità dell'oggetto viene scalato di un certo valore un contatore (definito dinamicamente in range) 47 | I giocatori che hanno eseguito un sacrificio vengono segnati in una lista. 48 | Quando il contatore è a zero avviene una verifica sugli attuali membri del team e la lista dei sacrifici, 49 | dall'unione vengono estratti al piu 5 giocatori che potranno entrare nel dedalo (che viene generato in quel momento). 50 | L'accesso al dedalo resterà aperto per 12 ore (dalla generazione). 51 | Il dedalo crolla al piu dopo 24 ore. 52 | Quando un giocatore entra nel dedalo si ritrova, nudo, nella prima stanza del dedalo. 53 | 54 | 55 | 56 | 0.b: attributi giocatori: 57 | • hp ( sono definiti in base alla vocazione 59 | • equip: arma, scudo, armatura, talismano 60 | • sacca: contiene al massimo 5 oggetti e non piu di 10 unità di peso (peso di S= 0, C = 1, IN= 2, NC= 3, R= 4 etcc…) 61 | • Stato (di tipo combattimento C… o movimento M… (avanzamento (MA), guarigione (MG), craft (MC), apprendimento (MI) ) o morte (DT) ) 62 | • Abilità (skill): mosse speciali in combattimento 63 | 64 | NOTA: se gli hp del giocatore scendono a 0 questi esce dal dedalo. Il ripristino avverrà dopo 3 ore. 65 | 66 | 67 | 68 | 69 | 1: Dedalo 70 | • i giocatori possono accedere indipendentemente al dedalo. 71 | • Il dedalo è coposto da un numero variabile di stanze. 72 | • Ogni stanza ha una descrizione che la definisce univocamente (calda, fredda, buia/accecante, floreale…) e, come minimo, una porta. 73 | • La descrizione ha una parte conclusiva che varia in base agli attributi del giocatore e può permettere di avere informazioni sulle porte. 74 | • Le potre sono di 4 tipi: 75 | 1) COMBATTIMENTO -> portano ad affrontare un mob (attributi sempre casuali bilanciati su 1 giocatore) o un boss (attributi fissi bilanciati su 5 giocatori), 76 | 2) AVANZAMENTO -> conducono ad un altra stanza con tempi di attesa variabili tra 1 e 15 minuti (cunicolo stretto, fanghiglia, vegetazione...). Eccezzionalente 30 e 45 minuti (crepaccio, lago sotterraneo...) 77 | 3) MALUS -> scatenano trappole: danni istantanei ad uno o piu attributi del giocatore 78 | 4) BONUS -> contengono scrigni o attrezzature o png aiutanti: oggetti (anche equipagiabili), bonus agli attributi o craft di oggetti 79 | 5) INQUIETANTE -> se nelle sacche dei giocatori nella stanza ci sono 3 campanelle, contiene il boss finale. Altrimenti mostra altre 3 porte. 80 | 6) DEL BOTTINO -> contiene 5 scrigni U. È l'unica porta dopo il boss finale (sconfitto). 81 | 82 | 83 | - COMBATTIMENTO: turni simmultanei con battito 84 | i battiti sono ogni 30 secondi (nei quali giocatori e npc possono scegliere la loro mossa). 85 | Un algoritmo decide l'ordine di azione (in base a velocità e contatore "azione successiva") 86 | - Mosse: 87 | 🐗 88 | Attacco: infligge un danno fisso in base all'attributo di attacco, potenziato da eventuale arma e decrementato da eventuale armatura. +1 all'ordine per l'azione successiva 89 | Attacco speciale: (buio, luce, fulmine, onda, fiamma) infligge un danno variabile in base alla combinazione di tipo ed attributi (attacco s. & difesa s.) tra attaccante e difensore. -1 all'ordine per l'azione successiva 90 | 👤 91 | Guardia: possibilità di ridurre o neutralizzare i danni da attacco semplice. -3 all'ordine per l'azione successiva 92 | Concentrazione: recupera energie. +2 all'ordine per l'azione successiva 93 | 👥 94 | Guarigione: possibilità di aumentare gli hp di un altro giocatore 95 | Supporto: possibilità di incrementare gli attributi di attacco di un giocatore 96 | 97 | - Stati: 98 | (CF) Fomentato: +2 all'ordine per l'azione successiva. Attivato da algoritmo. Termina dopo 2-5 battiti. 99 | (CS) Sgomentato: salta l'azione successiva. Attivato da attacco semplice. Termina alla fine del battito. 100 | (CP) Paralizzato: salta l'azione successiva. Attivato da attacco speciale. Termina dopo 1-3 battiti. 101 | (CB) Bruciato: danno fisso agli hp. Attivato da attacco speciale. Termina dopo 1-3 battiti. 102 | (CR) Rallentato: -2 all'ordine per l'azione successiva. Attivato da attacco speciale. Termina dopo 1-3 battiti. 103 | (CI) Inerme: Salta l'azione corrente. Attivato quando i danni subiti nel turno corrente sono > del 50% degli hp rimasti. Termina alla fine del battito. 104 | (CF) Furia (npc): Attacca da 2 a 5 giocatori. Attivato da algoritmo. Termina alla fine del battito. 105 | (CF) Furia (pg): Ripete l'attacco da 2 a 5 volte. Attivato da algoritmo o Supporto. Termina alla fine del battito. 106 | 107 | 108 | 109 | 110 | */ -------------------------------------------------------------------------------- /LootBot/utility/bot_response.js: -------------------------------------------------------------------------------- 1 | const util = require("./utils"); 2 | 3 | let stats = { 4 | loops: 0, 5 | messages: { 6 | delete: 0, 7 | recived: 0, 8 | sent: 0, 9 | edited: 0, 10 | file_sent: 0 11 | }, 12 | callBack: { 13 | recived: 0, 14 | answered: 0 15 | }, 16 | errors: { 17 | messages: { 18 | sent: 0, 19 | delete: 0, 20 | edited: 0, 21 | file_sent: 0 22 | }, 23 | callBack: { 24 | query: 0 25 | }, 26 | } 27 | }; 28 | 29 | async function manage(response, bot_instance) { 30 | stats.loops++; 31 | 32 | if (!util.isNully(response)) { 33 | let responses = normalize_response(response); 34 | 35 | await check_query_response(bot_instance, responses); // rispondo a query come prima cosa, se devo… 36 | 37 | // Rispetto al ciclo for, forEach consente di eseguire in parallelo le funzioni specifiche nell'array responses. 38 | responses.forEach(async (response) => { 39 | await switch_response(bot_instance, response); 40 | }) 41 | } 42 | } 43 | 44 | 45 | // Accessoria di manage 46 | // normalizza l'array su cui avviene il ciclo 47 | function normalize_response(response) { 48 | return Array.isArray(response) ? response : [response]; 49 | } 50 | 51 | // Accessoria di manage. È dove avviene lo switch nel singolo elemento di response 52 | async function switch_response(bot_instance, response) { 53 | // TO DELETE 54 | if ("toDelete" in response) { 55 | await manage_toDelete(bot_instance, response); 56 | } 57 | 58 | // TO SEND 59 | if ("toSend" in response) { 60 | if (response.toSend.message_text.length >= 3500) { // questo limite di 3500 andrebbe reso astratto 61 | let message_text_array = chunkSubstr(to_check, 100); // anche questo di 100… 62 | for (let l = 0; l < message_text_array.length; l++) { 63 | await manage_toSend(bot_instance, { 64 | chat_id: response.toSend.chat_id, 65 | message_text: message_text_array[l], 66 | options: response.toSend.options 67 | }); 68 | } 69 | } else { 70 | await manage_toSend(bot_instance, response); 71 | } 72 | } 73 | 74 | // TO EDIT 75 | if ("toEdit" in response) { 76 | await manage_toEdit(bot_instance, response); 77 | } 78 | 79 | // SEND FILE 80 | if ("sendFile" in response) { 81 | await manage_sendFile(bot_instance, response) 82 | } 83 | 84 | // SEND OBJECT (come senFile, ma prende un oggetto, lo parsa e lo trasforma in buffer prima di inviarlo...) 85 | if ("sendObject" in response) { 86 | await manage_sendObject(bot_instance, response) 87 | } 88 | } 89 | 90 | // Accessoria di manage (ma potrebbe esserlo solo di toSend?) 91 | // Divide il testo troppo lungo in un array 92 | function chunkSubstr(str, size) { // il text_msg e una lunghezza *limite* 93 | let str_array = str.split("\n"); 94 | const numChunks = Math.ceil(str_array.length / size); 95 | const chunks = new Array(numChunks); 96 | let str_copy = str_array.slice(); 97 | 98 | let tmp_size_counter = 0; 99 | let counter = 0; 100 | for (let i = 0; i < str_array.length; i++) { 101 | tmp_size_counter++; 102 | if (tmp_size_counter >= size || (tmp_size_counter > size / 2 && (str_copy[i].length == 0 || str_copy[i] == "\n"))) { 103 | chunks[counter] = str_array.slice(0, tmp_size_counter).join("\n"); 104 | str_array = str_array.slice(tmp_size_counter); 105 | counter++; 106 | tmp_size_counter = 0; 107 | } 108 | } 109 | return (chunks); //L'array finale. Ogni elemento contiene il testo_parziale (un messaggio) 110 | } 111 | 112 | 113 | // Seguono le chiamate ai metodi di bot_instance. Sono in try catch ed aggiornano i contatori in stats 114 | 115 | async function check_query_response(bot_instance, responses) { 116 | const query_result = responses.find((response) => "query" in response); 117 | 118 | let esit = false; 119 | 120 | if (query_result) { 121 | try { 122 | esit = await bot_instance.answerCallbackQuery( 123 | query_result.query.id, 124 | query_result.query.options 125 | ); 126 | } catch (err) { 127 | esit = false; 128 | stats.errors.callBack.query++; 129 | console.error(err); 130 | } 131 | } 132 | 133 | return esit; 134 | } 135 | 136 | 137 | async function manage_toDelete(bot_instance, response) { 138 | let esit; 139 | 140 | try { 141 | esit = await bot_instance.deleteMessage( 142 | response.toDelete.chat_id, 143 | response.toDelete.message_id 144 | ) 145 | stats.messages.delete++; 146 | } catch (err) { 147 | esit = false; 148 | stats.errors.messages.delete++; 149 | console.error(err.response.body); 150 | } 151 | 152 | return esit; 153 | } 154 | 155 | async function manage_toSend(bot_instance, response) { 156 | let esit; 157 | 158 | try { 159 | esit = await bot_instance.sendMessage( 160 | response.toSend.chat_id, 161 | response.toSend.message_text, 162 | response.toSend.options 163 | ) 164 | stats.messages.sent++; 165 | } catch (err) { 166 | esit = false; 167 | stats.errors.messages.sent++; 168 | console.error(err.response.body); 169 | } 170 | 171 | return esit; 172 | } 173 | 174 | async function manage_toEdit(bot_instance, response) { 175 | let esit; 176 | 177 | try { 178 | esit = await bot_instance.editMessageText( 179 | response.toEdit.new_text, 180 | response.toEdit.options 181 | ); 182 | stats.messages.edited++; 183 | } catch (err) { 184 | esit = false; 185 | stats.errors.messages.edited++; 186 | if (err.error_code && err.error_code != 400){ // messaggio già aggiornato... 187 | console.error(err.response.body); 188 | } 189 | } 190 | 191 | return esit; 192 | } 193 | 194 | // Questa non è stata ancora completata ne testata (sendObject è piu comoda…) 195 | async function manage_sendFile(bot_instance, response) { 196 | let esit; 197 | 198 | try { 199 | esit = await bot_instance.sendDocument( 200 | response.sendFile.chat_id, 201 | response.sendFile.file, 202 | response.sendFile.message, 203 | response.sendFile.options 204 | ); 205 | stats.messages.file_sent++; 206 | } catch (err) { 207 | esit = false; 208 | stats.errors.messages.file_sent++; 209 | console.error(err.response.body); 210 | } 211 | 212 | return esit; 213 | } 214 | 215 | async function manage_sendObject(bot_instance, response) { 216 | let esit; 217 | 218 | 219 | try { 220 | const objects_buffer = Buffer.from(response.sendObject.object, 'utf-8'); 221 | 222 | esit = await bot_instance.sendDocument( 223 | response.sendObject.chat_id, 224 | objects_buffer, 225 | response.sendObject.message, 226 | response.sendObject.options 227 | ); 228 | stats.messages.file_sent++; 229 | } catch (err) { 230 | esit = false; 231 | stats.errors.messages.file_sent++; 232 | console.error(err); 233 | } 234 | 235 | return esit; 236 | } 237 | 238 | module.exports = { 239 | manage: manage, 240 | stats: stats, 241 | responses: { 242 | query: () => ({ id: -1, options: { text: "", cache_time: 1, show_alert: false } }), 243 | toSend: (chat_id = -1) => ({ chat_id: chat_id, message_text: "", options: { parse_mode: "Markdown", disable_web_page_preview: true, reply_markup: { inline_keyboard: [] } } }), 244 | toEdit: () => ({ new_text: "", options: { chat_id: -1, message_id: -1, parse_mode: "Markdown", disable_web_page_preview: true, reply_markup: { inline_keyboard: [] } } }), 245 | sendFile: () => ({ chat_id: -1, message: {}, options: {}, file: {} }), 246 | toDelete: () => ({ chat_id: -1, message_id: -1 }), 247 | sendObject: (chat_id = -1, filename, object, caption_text = "…", text_keyboard = []) => ({ 248 | chat_id: chat_id, 249 | object: object, 250 | message: { 251 | caption: caption_text, 252 | parse_mode: "Markdown", 253 | reply_markup: { 254 | keyboard: text_keyboard, 255 | resize_keyboard: true, 256 | one_time_keyboard: true, 257 | } 258 | 259 | }, 260 | options: { filename: filename, contentType: "text/plain" } 261 | }) 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /LootBot/utility/config_sample.js: -------------------------------------------------------------------------------- 1 | const ex_config = require ("../../config.js"); 2 | 3 | module.exports = { 4 | // Questi dati sono pubblici e possono rimanere esposti 5 | phenix_id: 20471035, 6 | devs: { 7 | phenix_id: 20471035, 8 | nrc_id: 16964514, 9 | }, 10 | isDev: (id) => Object.values(this.devs).includes(id), 11 | 12 | database: { 13 | host: ex_config.dbhost, 14 | main_database: ex_config.dbdatabase, 15 | main_user: ex_config.dbuser, 16 | main_user_password: ex_config.dbpassword, 17 | }, 18 | 19 | beta_test: { 20 | master_craftsman: true, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /LootBot/utility/specifics/master_craftsman.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | //L'albero query di master_craftsman 3 | query_tree: { 4 | stmp: "CRAFTSMAN", 5 | guide: { 6 | stmp: "GUIDE", 7 | 8 | }, 9 | delete_message: { 10 | stmp: "DEL_MESSAGE", 11 | }, 12 | menu: { 13 | stmp: "MENU", 14 | }, 15 | smuggler: { 16 | stmp: "SMUGGLER", 17 | add_smuggler_to_list: { 18 | stmp: "SMUG_ADD", 19 | }, 20 | check_missing: { 21 | stmp: "SM_MISSING", 22 | }, 23 | }, 24 | assault: { 25 | stmp: "ASSAULT", 26 | missing: { 27 | stmp: "UP_MISSING", 28 | add_missing_to_list: { 29 | stmp: "UP_ADDMISS", 30 | }, 31 | }, 32 | all: { 33 | stmp: "UP_ALL", 34 | add_all_to_list: { 35 | stmp: "UP_ADDALL", 36 | } 37 | }, 38 | }, 39 | list: { 40 | stmp: "LIST", 41 | main_view: { 42 | stmp: "MAIN_VIEW", 43 | }, 44 | set_prefix: { 45 | stmp: "PREFIX", 46 | }, 47 | items_page: { 48 | stmp: "ITEMS_PAGE", 49 | }, 50 | set_rarity: { 51 | stmp: "SET_RARITY", 52 | }, 53 | show_list: { 54 | stmp: "SHOW_LIST", 55 | }, 56 | custom_list: { 57 | stmp: "CUSTOM_LIST", 58 | }, 59 | download_list: { 60 | stmp: "DOWNLOAD_LIST", 61 | }, 62 | clear_list: { 63 | stmp: "CLEAR", 64 | confirm: { 65 | stmp: "CLEAR_CONFIRM", 66 | } 67 | }, 68 | censure: { 69 | stmp: "CENSURE", 70 | remove: { 71 | stmp: "REMOVE", 72 | }, 73 | set_censure: { 74 | stmp: "SET_CENSURE", 75 | } 76 | }, 77 | add_to_list: { 78 | stmp: "ADD", 79 | }, 80 | set_preserve_bool: { 81 | stmp: "PRESERVE_CRAFTED", 82 | change: { 83 | stmp: "CHANGE", 84 | } 85 | } 86 | }, 87 | validate: { 88 | stmp: "VALIDATE", 89 | show_missing: { 90 | stmp: "SHOW_MISSING", 91 | }, 92 | show_used: { 93 | stmp: "SHOW_USED", 94 | used_crafted: { 95 | stmp: "USED_CRAFTED", 96 | }, 97 | used_base: { 98 | stmp: "USED_BASE", 99 | }, 100 | all_used: { 101 | stmp: "ALL_USED", 102 | }, 103 | }, 104 | print_manual_craft_line: { 105 | stmp: "PRINT_MANUAL_LINE", 106 | }, 107 | craft_line_commit: { 108 | stmp: "CRAFT_LINE_COMMIT", 109 | }, 110 | } 111 | } 112 | } -------------------------------------------------------------------------------- /LootBot/utility/specifics/necro_descent.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | //L'albero query di master_craftsman 3 | query_tree: { 4 | stmp: "NECRO_D", 5 | guide: { 6 | stmp: "GUIDE", 7 | }, 8 | altar: { 9 | stmp: "ALTARE", 10 | descent: { 11 | stmp: "DESCENT", 12 | }, 13 | sacrifice: { 14 | stmp: "SACRIFICE_MENU", 15 | // qui i bottoni per il menu sacrificio 16 | }, 17 | 18 | }, 19 | maze:{ 20 | stmp: "MAZE", 21 | change_facing: { 22 | stmp: "CHANGE_FACING", 23 | }, 24 | goto_gate: { 25 | stmp: "GOTO_GATE", 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /LootBot/utility/utils.js: -------------------------------------------------------------------------------- 1 | 2 | /* Questo modulo ospita: 3 | funzioni di utilità generiche 4 | struttura database e query (quest'ultima con funzione formatter) 5 | */ 6 | const master_craftsman_utils = require("./specifics/master_craftsman") 7 | const nd_utils = require("./specifics/necro_descent") 8 | 9 | // In questo oggetto è tenuta traccia delle strutture nelle tabelle del database 10 | const db_structures = { 11 | players:{ 12 | telegram_id: "account_id", 13 | main_info: { 14 | account_id: "account_id", 15 | id: "id", 16 | nickname: "nickname", 17 | gender: "gender", 18 | reborn: "reborn", 19 | money: "money", 20 | class: "class", // Vocazione 21 | exp: "exp", 22 | }, 23 | craft: { 24 | craft_point: "craft_count", 25 | weekly_point: "craft_week", 26 | daily_pint: "craft_day", 27 | }, 28 | dungeon_info: {}, 29 | cave_info: {}, 30 | mission_info: {}, 31 | maps_info: {}, 32 | equip_info: { // Primo esempio 33 | sword: "weapon_id", 34 | armor: "weapon2_id", 35 | shield: "weapon3_id" 36 | }, 37 | equip_item_info: { // Primo esempio 38 | rarity: "rarity", 39 | sword_power: "power", 40 | armor_power: "power_armor", 41 | shield_power: "power_shield" 42 | } 43 | }, 44 | smuggler: { 45 | item_id: "item_id", 46 | }, 47 | assault: { 48 | places: "place_id", 49 | item_id: "item_id", 50 | item_quantity: "quantity", 51 | phase: "phase" 52 | }, 53 | teams:{ 54 | team_id: "team_id", 55 | player_id: "player_id" 56 | }, 57 | ability:{ 58 | ability_id: "ability_id", 59 | player_id: "player_id", 60 | ability_level: "ability_level", 61 | }, 62 | abiliti_list:{ 63 | ability_id: "id", 64 | ability_power: "val" 65 | }, 66 | items: { 67 | key: "id", 68 | name: "name", 69 | rarity: "rarity", 70 | craftable: "craftable", 71 | }, 72 | inventory: { 73 | player_id: "player_id", 74 | item_id: "item_id", 75 | quantity: "quantity", 76 | durability: "durability", 77 | max_durability: "durability_max", 78 | }, 79 | craft: { 80 | needed: { 81 | 1: "material_1", 82 | 2: "material_2", 83 | 3: "material_3" 84 | }, 85 | key: "material_result" 86 | }, 87 | printObject: (object) => Object.values(object).join(", ") 88 | } 89 | 90 | // In questo oggetto è tenuta traccia dei percorsi delle query. (e passandolo a generate_callback_rute è possibile generarli) 91 | const query_tree = { 92 | /* 93 | template: 94 | key: { 95 | stmp: "keyname", 96 | 97 | }, 98 | */ 99 | 100 | // Questo oggetto va astratto in un modulo dedicato... ma si dovrebbe trovare una soluzione per i nomi duplicati che non sia l'attuale (e poco efficace) sistema di (ultimo, primo) 101 | // Così come è non è scalabile... :( 102 | master_craftsman: master_craftsman_utils.query_tree, 103 | necro_descent: nd_utils.query_tree, 104 | } 105 | 106 | // In last_rute l'ultimo stmp della query. In sub_tree l'albero su cui fare la ricerca <- puo essere l'intero query_tree o solo una parte (ad esempio query_tree.mastercraftsman.menu) 107 | function generate_callback_rute(last_rute, sub_tree) { 108 | const callback_full_rute = []; 109 | findIn_query_tree(sub_tree, [], last_rute, callback_full_rute); 110 | return callback_full_rute.length > 1 ? callback_full_rute.join(":") : callback_full_rute[0]; 111 | } 112 | 113 | // Funzione ricorsiva accessoria di generate_callback_rute 114 | function findIn_query_tree(node, tmp_line, rute_string, full_rute) { 115 | if (node.hasOwnProperty("stmp")) { 116 | tmp_line.push(node.stmp); 117 | if (node.stmp === rute_string) 118 | full_rute.push(...tmp_line); 119 | } 120 | 121 | for (const chiave in node) { 122 | if (typeof node[chiave] === 'object') 123 | findIn_query_tree(node[chiave], tmp_line, rute_string, full_rute); 124 | } 125 | 126 | if (tmp_line.length > 0) 127 | tmp_line.pop(); 128 | } 129 | 130 | const query_structure = { 131 | generate_callback_rute: generate_callback_rute, 132 | query_tree: query_tree 133 | } 134 | 135 | 136 | 137 | 138 | // ACCESSORIE GENERALI 139 | 140 | // vera solo per value === null 141 | function isNull (value) {return typeof value === "object" && !value}; 142 | 143 | // vera se value === null o undefined 144 | function isNully (value) {return isNull(value) || typeof value === 'undefined'}; 145 | 146 | module.exports = { 147 | db_structures: db_structures, 148 | query_structure: query_structure, 149 | isNull: isNull, 150 | isNully: isNully, 151 | simple_number_formatter: (number) => number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, "."), 152 | player_max_money: 1000000000, // qui perche non saprei dove metterlo :( 153 | master_craftsman_cost_multiplier: 3, 154 | } -------------------------------------------------------------------------------- /LootBot/views/errors.js: -------------------------------------------------------------------------------- 1 | // Le stringhe per gli errori. 2 | const strings = { 3 | title: "*Woops!*\n\n", 4 | contact_admin: "se il problema persiste considera di contattare l'amministratore", 5 | master_craftsman: { 6 | craft_line: "`▸ Errore generando la linea craft per {#}`" 7 | }, 8 | players: { 9 | bad_input: `▸ Errore input: {#}`, 10 | load_mainInfo: `▸ Errore caricando player\\_main\\_info per account\\_id: {#}`, 11 | load_teamId: `▸ Errore cercando di ottenere team_id per player_id: {#}`, 12 | load_abilities: `▸ Errore cercando di ottenere la lista talenti per player_id: {#}`, 13 | load_craftsman_info: `▸ Errore accedendo alle preferenze craft per account_id: {#}`, 14 | load_equipInfo: `▸ Errore caricando player_info per account_id: {#}`, 15 | 16 | }, 17 | items: { 18 | load_allItems: `▸ Errore nel caricamento in ram della lista oggetti. Le funzioni di craft non sono disponibili`, 19 | load_needed: `▸ Errore nel caricamento in ram dei necessari per l'oggetto {#}`, 20 | load_equipInfo: `▸ Errore cercando le informazioni per l'oggetto equipaggiabile {#}`, 21 | 22 | }, 23 | inventory: { 24 | update_items: `▸ (oggetti) Errore nel aggiornamento del database per:\n{#}`, 25 | load_fullInventory: `▸ Errore nel caricamento dello zaino completo per l'utente {#}`, 26 | load_equip: `▸ Errore nel caricamento dell'equipaggiamento per l'utente {#}`, 27 | item_quantity: `▸ Errore nella variabile di ingresso item_quantity\n{#}` 28 | }, 29 | database: { 30 | closing: '▸ Errore durante la chiusura della connessione al database:', 31 | query: '▸ Errore durante la query: {#}' 32 | } 33 | } 34 | 35 | module.exports = { 36 | // Funzione per stampare un messaggio, sostituendone {#} con una variabile 37 | print: (error_message, variable) => typeof variable !== "undefined" ? error_message.split("{#}").join(variable.split("_").join("\\_")).split("_").join("\\_") : error_message, // sostituisce a {#} la variabile in ingresso 38 | // Funzione per generare una stringa a partire dagli elementi di un oggetto (da passare come variabile a print) 39 | flatted_function_paparameters: (array) => array.map(obj => { 40 | return Object.entries(obj).map(([key, value]) => `> ${key}: ${value}\n`); 41 | }).flat().join(""), 42 | str: strings // oggetto con le stringhe, divise per categoria 43 | } -------------------------------------------------------------------------------- /LootBot/views/specific/master_craftsman.js: -------------------------------------------------------------------------------- 1 | const query_util = require("../../utility/utils").query_structure 2 | const sub_tree = query_util.query_tree.master_craftsman; 3 | 4 | module.exports = { 5 | title: "Maestro Artigiano", 6 | keyboard_buttons: { 7 | delete_message: { text: "ⓧ", callback_data: query_util.generate_callback_rute(sub_tree.delete_message.stmp, sub_tree) }, 8 | 9 | back_to_menu: { text: "↵", callback_data: query_util.generate_callback_rute(sub_tree.menu.stmp, sub_tree) }, 10 | master_craftsman_guide: { text: "ⓘ", callback_data: query_util.generate_callback_rute(sub_tree.guide.stmp, sub_tree) }, 11 | assault_view_main: { text: "🐺", callback_data: query_util.generate_callback_rute(sub_tree.assault.stmp, sub_tree) },// "Assalto", 12 | assault_show_missing: { text: "Controlla lo zaino", callback_data: query_util.generate_callback_rute(sub_tree.assault.missing.stmp, sub_tree) },// "Assalto", 13 | assault_show_all: { text: "Tutti", callback_data: query_util.generate_callback_rute(sub_tree.assault.all.stmp, sub_tree) },// "Assalto", 14 | assault_addMissing_to_list: { text: "Aggiungi alla lista", callback_data: query_util.generate_callback_rute(sub_tree.assault.missing.add_missing_to_list.stmp, sub_tree) },// "Assalto", 15 | assault_addAll_to_list: { text: "Aggiungi alla lista", callback_data: query_util.generate_callback_rute(sub_tree.assault.all.add_all_to_list.stmp, sub_tree) },// "Assalto", 16 | smuggler_view_main: { text: "🔩", callback_data: query_util.generate_callback_rute(sub_tree.smuggler.stmp, sub_tree) },// "Assalto", 17 | smuggler_add_offert: { text: "Aggiungi alla lista", callback_data: query_util.generate_callback_rute(sub_tree.smuggler.add_smuggler_to_list.stmp, sub_tree) },// "Assalto", 18 | smugglier_check_missing: { text: "Controlla lo zaino", callback_data: query_util.generate_callback_rute(sub_tree.smuggler.check_missing.stmp, sub_tree) },// "Assalto", 19 | 20 | // ↧ 21 | list_view_main: { text: "📝", callback_data: query_util.generate_callback_rute(sub_tree.list.main_view.stmp, sub_tree) },// "Compila la lista", 22 | delete_list: { text: "🗑", callback_data: query_util.generate_callback_rute(sub_tree.list.clear_list.confirm.stmp, sub_tree) }, // "Cancella la lista", 23 | download_list: { text: "↧", callback_data: query_util.generate_callback_rute(sub_tree.list.download_list.stmp, sub_tree) }, // "Cancella la lista", 24 | 25 | add_to_list: { text: "", callback_data: query_util.generate_callback_rute(sub_tree.list.add_to_list.stmp, sub_tree) }, // "bottone oggetto aggiungi a lista", 26 | show_items_list: { text: "📋", callback_data: query_util.generate_callback_rute(sub_tree.list.show_list.stmp, sub_tree) }, // "Cancella la lista", 27 | validate_list: { text: "Consegna la Lista", callback_data: query_util.generate_callback_rute(sub_tree.validate.stmp, sub_tree) }, // "Cancella la lista", 28 | set_rarity: { text: "⭑", callback_data: query_util.generate_callback_rute(sub_tree.list.set_rarity.stmp, sub_tree) }, // "Cancella la lista", 29 | preserve_remove: { text: "🎒", callback_data: query_util.generate_callback_rute(sub_tree.list.set_preserve_bool.change.stmp, sub_tree) }, // "Cancella la lista", 30 | preserve_confirm: { text: "🙅‍♂️", callback_data: query_util.generate_callback_rute(sub_tree.list.set_preserve_bool.change.stmp, sub_tree) }, // "Cancella la lista", 31 | index_button: { text: "¶", callback_data: query_util.generate_callback_rute(sub_tree.list.set_prefix.stmp, sub_tree) }, // "Cancella la lista", 32 | items_page_button_forward: { text: "→", callback_data: query_util.generate_callback_rute(sub_tree.list.items_page.stmp, sub_tree) }, // "Cancella la lista", 33 | items_page_button_backward: { text: "←", callback_data: query_util.generate_callback_rute(sub_tree.list.items_page.stmp, sub_tree) }, // "Cancella la lista", 34 | censure_view_remove: { text: "◎", callback_data: query_util.generate_callback_rute(sub_tree.list.censure.remove.stmp, sub_tree) }, // "Cancella la lista", 35 | censure_view_set: { text: "◉", callback_data: query_util.generate_callback_rute(sub_tree.list.censure.set_censure.stmp, sub_tree) }, // "Cancella la lista", 36 | 37 | show_craft_missing: { text: "Mancanti", callback_data: query_util.generate_callback_rute(sub_tree.validate.show_missing.stmp, sub_tree) }, // "Cancella la lista", 38 | show_craft_used: { text: "Consumati", callback_data: query_util.generate_callback_rute(sub_tree.validate.show_used.stmp, sub_tree) }, // "Cancella la lista", 39 | print_manual_craft_line: { text: "Craft manuale", callback_data: query_util.generate_callback_rute(sub_tree.validate.print_manual_craft_line.stmp, sub_tree) }, // "Cancella la lista", 40 | show_craft_used_base: { text: "Base", callback_data: query_util.generate_callback_rute(sub_tree.validate.show_used.used_base.stmp, sub_tree) }, // "Cancella la lista", 41 | show_craft_used_base: { text: "Creati", callback_data: query_util.generate_callback_rute(sub_tree.validate.show_used.used_crafted.stmp, sub_tree) }, // "Cancella la lista", 42 | show_craft_used_base: { text: "Raccogli lista", callback_data: query_util.generate_callback_rute(sub_tree.validate.show_used.all_used.stmp, sub_tree) }, // "Cancella la lista", 43 | commit_craft: { text: "Commissiona il craft", callback_data: query_util.generate_callback_rute(sub_tree.validate.craft_line_commit.stmp, sub_tree) }, // "Cancella la lista", 44 | 45 | 46 | }, 47 | beta_tester: { 48 | user_message: "La fucina dell'Artigiano è in fase di allestimento…\n", 49 | insert_success: " account abilitato a: `Mastro Artigiano`", 50 | empty_list: "Nessun betatester per questa sessione", 51 | show_list: "*Lista dei betatester*\n_per questa sessione_\n\n", 52 | query_user_not_listed: "La tua sessione di testing è terminata…", 53 | }, 54 | menu: { 55 | introduction: "...tra il fragore di incudini e martelli una figura emerge dalle ombre.\nCon sguardo penetrante il Mastro Artigiano ti fissa...\n", 56 | is_banned: "...sembra deluso e dispiaciuto…", 57 | not_allowed: "Abbassa infine gli occhi, si gira e senza voltarsi torna al suo battere e forgiare…", 58 | wellcome: "Benvenut*", 59 | wellcome_new: "Viandante", 60 | wellcome_back: "Bentornat*", 61 | waiting_phrases: [ 62 | "Hai bisogno d'aiuto? … Hai letto il cartello?", 63 | "Si si, resta pure qui a guardarmi...", 64 | "Compila la tua lista, viandante", 65 | "Sto aspettando…", 66 | "Questi nani sono tremendi…\nPosso fare qualcosa per te?", 67 | "Avrei da lavorare…", 68 | "Se hai qualche cosa da creare, aggiungilo alla lista…", 69 | "Ancora qui?", 70 | "...Ohibo!\npensavo non ci fosse piu nessuno…" 71 | ], 72 | long_list_phrases: [ 73 | "Ci vorrà un eternità a creare quella roba li…", 74 | "Urca!", 75 | "Boja fauss!", 76 | "Che lista!\nE tu ha tutto il necessario?", 77 | "Spero tu non abbia altro da aggiungere…", 78 | "Prima iniziamo, prima finiamo…", 79 | "La tua lista sembra impegnativa, viandante…", 80 | "Ne avranno da lavorare, i nanetti…" 81 | ], 82 | short_list_phrases: [ 83 | "Se quello è quello che hai da creare, ci vorrà meno che un batter d'occhi", 84 | "Tutta qui la tua lista?", 85 | "Tutta qui, la tua lista?", 86 | "Poche cose ma buone…", 87 | "Sarà un lavoretto da nulla…", 88 | "Tutto qui!?", 89 | "Quindi è questa la tua lista?", 90 | "Queste cose le può creare anche un nano!" 91 | 92 | ], 93 | failed_validation_phrases: [ 94 | "Ancora qui?", 95 | "Vuoi riporovare?", 96 | "Finito di farmi perdere tempo?", 97 | ] 98 | }, 99 | guide: { 100 | title: "Liste craft 📋", 101 | text: "Aggiungi oggetti alla lista e consegnala al Mastro Artigiano che analizzerà la richiesta e ne valuterà il costo…", 102 | navigation_title: "Scorrimento della lista creati", 103 | navigation_rarity: "Seleziona una rarità: ⭑", 104 | navigation_prefix: "Seleziona un sottoelenco: ¶", 105 | 106 | commit_text: "Ed anche tu valuta attentamente costo e oggetti utilizzati…\nSe sei soddisfatt* 'Commissiona' il craft, riceverai immediatamente gli oggetti richiesti.", 107 | 108 | settings_title: "Attualmente:", 109 | censure_is_set: "• Scorrerai tra tutti i creabili: ◉", 110 | censure_unset: "• Scorrerai solo tra gli oggetti che puoi creare: ◎", 111 | preserve_is_set: "• Consegnerai al Mastro solo oggetti base", 112 | preserve_unset: "• Se serviranno, il Mastro potrà consumare creati dal tuo zaino", 113 | 114 | 115 | 116 | 117 | }, 118 | smuggler: { 119 | title: "Offerte di Contrabbando 🔩", 120 | items_needed: "Richiesta:", 121 | items_added: "Aggiunte alla lista", 122 | item_missing: "❌\nSembra tu non abbia", 123 | has_item: "✅\nHai già", 124 | 125 | 126 | errors: { 127 | title: "🔩\nWoops!\n\n", 128 | nothing_to_do: "Sembra non ci siano più offerte per te…", 129 | 130 | } 131 | 132 | }, 133 | assault: { 134 | title: "Potenziamento Postazione 🐺", 135 | items_needed: "oggetti richiesti", 136 | items_added: "oggetti aggiunti alla lista", 137 | 138 | errors: { 139 | title: "🐺\nWoops!\n\n", 140 | no_team: "Sembra che tu non sia piu in un team…", 141 | not_now: "Torna durante il giorno della preparazione…", 142 | not_in_place: "Non sei ancora in postazione!", 143 | nothing_to_do: "Sembra non ci siano lavori da fare nella tua postazione\n\n💪️️", 144 | nothing_important_to_do: "Hai gia copie a sufficenza\n\n💪️️" 145 | 146 | } 147 | 148 | }, 149 | list: { 150 | title: "Lista commissione", 151 | edit_moji: "📝", 152 | list_moji: "📋", 153 | empty_list: "• Ancora nessun oggetto in elenco", 154 | list_length: "• Oggetti nell'elenco:", 155 | list_total_quantity: "• Quantità totale:", 156 | is_preserving: "Solo oggetti base", 157 | is_not_preserving: "Userai anche i creati nello zaino", 158 | selected_rarity: "Rarità", 159 | selected_prefix: "Prefisso", 160 | show_list_length: "oggetti", 161 | craftables_in_list: "oggetti creabili", 162 | 163 | list_clear: "Hai stralciato l'elenco craft…", 164 | censure_set: "Ti saranno mostrati solo i creati compatibili con la tua rinascita", 165 | censure_remove: "Puoi scorrere liberamente tra tutti i creabili", 166 | 167 | download_list: "• Scarica l'elenco ↧", 168 | 169 | rarity_select: "• Seleziona una rarità", 170 | serarch_info: "o rispondi a questo messaggio con il nome (anche parziale) di un oggetto", 171 | 172 | prefix_select: "• Seleziona un prefisso indice", 173 | 174 | }, 175 | search: { 176 | input: "• Input:", 177 | match: "match", 178 | no_match: "nessun match", 179 | }, 180 | edit_quantity: { 181 | guide: "_gestire le quantità_\n\nRispondi al messaggio specificando in ogni linea il nome (anche parziale) di un oggetto e la quantità che vuoi impostare.\nPuoi anche completare il comando con un operatore tra \`x\`, \`+\`, \`-\`" 182 | }, 183 | validate: { 184 | give_list: "Consegni la lista commissione al mastro…", 185 | unable: { 186 | unable_moji: "❌", 187 | first_line: "Il Mastro ti osserva, sembra schifato…", 188 | quote: "Non puoi permetterti questa roba!", 189 | conclusion: "Straccia la tua lista e si volta…", 190 | too_much: "Mi spiace,\nma è davvero troppo lavoro.…", 191 | too_much_conclusion: "L'Artigiano stringe la lista tra le mani accartocciandola" 192 | }, 193 | introduction: "Il Mastro Artigiano prende la tua lista e gli getta una rapida occhiata...", 194 | loops: { 195 | just_one: "Un solo craft?\nAvresti potuto anche farlo tu…", 196 | not_much: "Fiuuu!", 197 | a_fiew: "Mmh… !", 198 | a_lot: "Urca!" 199 | }, 200 | quote_on_items_quantity: "oggetti?\nVediamo un po…", 201 | inventory_lookup: "Ti si avvicina e comincia a rovistare nel tuo zaino. In breve è di nuovo in piedi davanti a te", 202 | inventory_no_money: [ 203 | "Si potrebbe fare... ma mancano i fondi!", 204 | "Potremmo inizaire anche subito, se solo avessi gli edollari necessari…", 205 | "Ma con quali soldi?", 206 | "Ok, ma servono i dindi", 207 | "Mi spiace, ma sembra che tu non possa peretterti la spesa..." 208 | ], 209 | inventory_no_missing: [ 210 | "Si può fare!", 211 | "Andata!", 212 | "Non dovrei metterci poi molto…", 213 | "Va bene", 214 | "…E va bene!", 215 | "Mmh… !\n…Va bene!", 216 | "D'accordo!", 217 | "Si, si può fare.", 218 | "Si, si, si…\nSi può fare", 219 | "E sia!", 220 | "Possiamo anche inizaire subito…", 221 | ], 222 | inventory_is_missing: { 223 | not_much: "Peccato! Manca della roba qui…", 224 | a_fiew: "Servirà qualche altro base però!", 225 | a_lot: "La tua è una richiesta ambiziosa…\nTi ho scritto una lista di quello che ti manca", 226 | }, 227 | inventory_is_using_base: { 228 | not_much: "Mi serviranno solamente", 229 | a_fiew: "Non userò molto", 230 | a_lot: "Ti ho scritto un elenco di quello che mi servirà…" 231 | }, 232 | inventory_is_using_crafted: { 233 | not_much: "...ed userò anche", 234 | a_fiew: "...oltre a", 235 | }, 236 | craft_commission: { 237 | introduction: "Creando questa roba da sol* spenderesti", 238 | commission: "§ ma...", 239 | commission_excuses: [ 240 | "La Fenice", 241 | "gli gnomi", 242 | "ho famiglia", 243 | "sai, il mio gatto", 244 | "sai, quello gnomo è incinto!", 245 | "il costo delle materie prime", 246 | "il costo delle materie prime…\n…e l'assicurazione per i nani…\n Mi spiace", 247 | "portare avanti questo posto è un impresa!\n", 248 | "tutto ha un costo", 249 | "cioè, vorrei… ma… ", 250 | "ci sono le tasse alla Fenice e…\n", 251 | "hai visto che Fucina che abbiamo?", 252 | "Il contrabbando va forte, e noi restiamo senza fornitori… cioè… insomma… ", 253 | "c'è il nostro lavoro, e il mana…\ne le tasse", 254 | "c'è il nostro lavoro, e il pozzo…\ne gli gnomi", 255 | "c'è il nostro lavoro, e la Fenice…\ne la fenice… La Fenice!\n", 256 | 257 | 258 | ], 259 | commission_end: "…devo chiederti altri " 260 | }, 261 | craft_total_cost: "Il costo totale sarà", 262 | 263 | craft_pc: "Guadagnerai", 264 | too_expensive_craft_cost: "Dovresti spendere", 265 | too_expensive_craft_pc: "Guadagneresti", 266 | show_used: { 267 | quote: "Sono troppi oggetti quelli che consumeresti…\nTieni, ecco un riepilogo", 268 | not_much: "Non consumerai granche!", 269 | base: { 270 | none: "Nemmeno un oggetto base", 271 | just_one: "Un solo oggetto base", 272 | default: "oggetti base" 273 | }, 274 | crafted: { 275 | none: "nemmeno un creato", 276 | just_one: "un solo creato", 277 | default: "oggetti creati" 278 | }, 279 | } 280 | 281 | }, 282 | manual_craft: { 283 | quick_evade: [ 284 | "«Non che smaniassi dalla voglia di farlo io per te!»", 285 | "«Noi qui siam lieti di aiutare»", 286 | "«Ecco a te»", 287 | "«Se proprio ci tieni…»", 288 | "«Eppure questi gnomi hanno tanto bisogno di lavorare…»", 289 | "«Fa sempre bene mettersi in gioco»", 290 | "«Apprezzo chi preferisce farsi le cose da se»", 291 | "«Non è un felice lavoro, va se va proprio fatto…»", 292 | "Risparmierai qualcosina, che di questi tempi…", 293 | ], 294 | }, 295 | list_print: { 296 | current_list_file_name: "Lista Commissione.txt", 297 | manual_craft_file_name: "Lista Commissione.txt", 298 | manual_line: "Linee per il craft manuale", 299 | manual_line_short: "Linee per il craft manuale", 300 | manual_line_index: "Crea ", 301 | file_name: "Riepilogo.txt", 302 | target_items: "Obbiettivo craft", 303 | missing: "Mancanti", 304 | used_items: "\tconsumati", 305 | base: "Oggetti Base", 306 | crafted: "Oggetti Creati", 307 | list_tab: "\t\t", 308 | line: "-", 309 | all_used_items: "Oggetti consumati", 310 | craft_cost: "Edollari spesi", 311 | craft_commission: "Costi di commissione", 312 | craft_gained_pc: "Punti craft ottenuti" 313 | 314 | }, 315 | commit: { 316 | money_controll: "Il Mastro Artigiano ti squadra da capo a piedi\n\n«Torna quando avrai recuperato gli edollari necessari…»", 317 | load_controll: "Il Mastro Artigiano resta a bocca aperta...\n\n«Sembra abbia problemi nel controllare il tuo zaino…»", 318 | used_items_controll: "Il Mastro Artigiano ti squadra da capo a piedi, contrariato.\n\n«Stai cercando di fregarmi?\nTorna quando avrai recuperato gli oggetti necessari…»", 319 | target_items_controll: "Il Mastro Artigiano sembra confuso…\n\n«Mi spiace, ma a pensarci bene non posso accettare la commissione\nFiniresti con l'avere troppi oggetti nello zaino…»", 320 | 321 | report_title: "Craft Report", 322 | file_name: "CraftReport.txt", 323 | 324 | text: "_…osservi il Mastro Artigiano raccogliere dal tuo zaino quello che gli serve.\nLo butta in una sacca…\n" + 325 | "…resti in silenzio mentre svuota il contenuto di quella sacca nel grande pozzo davanti alla sua fucina\n" + 326 | "…resti fermo.\nResti a guardare…_", 327 | bizzarre_events: [ // (questo quando erano 22...) Se ho fatto bene i conti sono 2024 combinazioni se ne mostro 3, 12.650 mostrandone 4. Non sono sicuro di quanto sia la somma… 328 | "…ci sono gnomi, sono gnomi quelli?", 329 | "…ci sono gnomi che sbattono.", 330 | "…ci sono gnomi che sbattono.", 331 | "…ci sono gnomi che squotono.", 332 | "…ci sono gnomi che corrono.", 333 | "…ci sono gnomi che urlano.", 334 | "…ci sono nani che battono.", 335 | "…ci sono nani che zampettano.", 336 | "…due nani si azzuffano…", 337 | "…quei nani stanno, stanno...", 338 | "…c'è un nano che sembra essersi ferito…\nSanguina?\n....aiutatelo!", 339 | "…senti l'odore del ferro riscaldato", 340 | "…senti il fetore del mana fuso", 341 | "…senti il calore del plasma incandescente", 342 | "…senti il calore degli ioni", 343 | "i tuoi occhi provano a seguire la scena…", 344 | "le tue orecchie iniziano a fischiare…", 345 | "lapilli di materiale fuso zampettano nell'aria…", 346 | "c'è un pony?", 347 | "senti freddo…", 348 | "fa freddo…", 349 | "buio!", 350 | "luce!", 351 | "qualche cosa di celeste?!\nMETALLO!", 352 | "qualche cosa di diabolico e galattico...", 353 | "qualche cosa di acciaio...", 354 | "scalpello, avorio", 355 | "Scalpello, laccio antico, scalpello...\nSCALPELLO!", 356 | "un Rubino Primordiale!", 357 | "Martello Acciaio.\nÈ il suo nome...\nMartello Acciaio\n", 358 | "Sbang, Crack, Sdong!", 359 | "Sbang, Sdong!", 360 | "Crack, Sbang, Sdong!", 361 | "Zunk, Crack, Yonk, fiiix!", 362 | "Aaaaaaaa!" 363 | ], 364 | 365 | ending_text: "_E in un attimo è tutto finito\n" + 366 | "non riesci a vedere l'artigiano, ti sei distratto e l'hai perso…\n" + 367 | "Ai tuoi piedi c'è uno gnomo\n" + 368 | "Lo guardi, ti guarda, ti butta addosso una cartaccia…_\n" + 369 | "«È il report», _dice…_\n" + 370 | "«La tua roba è gia nello zaino!»\n_Sparisce…_" 371 | 372 | 373 | }, 374 | errors: { 375 | title: "Woops!", 376 | beta_wrong_input: "Sintassi: `/craftbeta` _?id\\_utente ?id\\_utente …_\n\nEsempio:\n> `/craftbeta 354140824 153738969`" 377 | } 378 | } -------------------------------------------------------------------------------- /LootBot/views/specific/necro_descent.js: -------------------------------------------------------------------------------- 1 | // Questo è il modulo che cerca di racchiudere tutte le stringhe di necro_descent 2 | 3 | const query_util = require("../../utility/utils").query_structure 4 | const sub_tree = query_util.query_tree.necro_descent; 5 | 6 | 7 | module.exports = { 8 | title: "Discesa agli Inferi 👹", 9 | beta_tester: { 10 | user_message: "Ma l'era dei sacrifici è finita da tempo…\nDicono…\n", 11 | insert_success: " account abilitato a: `Discesa agli Inferi`", 12 | empty_list: "Nessun betatester per questa sessione", 13 | show_list: "*Lista dei betatester*\n_per questa sessione_\n\n", 14 | query_user_not_listed: "La tua sessione di testing è terminata…", 15 | }, 16 | altar: { 17 | title: "Altare Sacrificale ⛩️", 18 | introduction: "L'antico sacrario svetta nel cuore della magione tra volute di fumo d'incenso…", 19 | gate_is_open: "Al centro un terrificante squarcio nella pietra sembra condurre direttamente giù… nelle viscere dell'inferno…" 20 | 21 | }, 22 | maze: { 23 | rooms_icons: ["①", "②", "③", "④", "⑤", "⑥", "⑦", "⑧", "⑨", "⑩"], 24 | player: { 25 | already_in_maze: "🌬", 26 | current_room: { 27 | single_player: "Sei in", 28 | multy_player: "Siete in", 29 | facing: { front: "Davanti a te:", left: "Alla tua sinistra", right: "Alla tua destra", back: "Dietro di te" }, 30 | no_gates: ["Il nulla", "Solo silenzio", "Il buio", "Nero"] 31 | }, 32 | firs_jump_impressions: ["Buio.", "Solo buio.", "Brividi.", "Abisso.", "Oscurità", "Soltanto oscurità.", "Nel nulla.", "Silenzio.", "Non un rumore.", "Coraggio?", "Nel Vuoto…", "Sconsideratezza…", "Senza percepire la caduta…", "Il corpo non ha peso…"] 33 | }, 34 | nodes: { 35 | types: { final: 0, tunnel: 1, passage: 2, way: 3, room: 4, blind: 5, special: 6 }, 36 | dimensions_types: { micro: 0, small: 1, medium: 2, large: 3 }, 37 | gate_types: { narrow: 0, standard: 1, large: 2 }, 38 | gate_direction: { nord: 0, sud: 1, est: 2, ovest: 3 }, 39 | node_description: { 40 | types: ["cunicolo", "passaggio", "corridoio", "salaone",], 41 | dimensions: ["un", "un piccolo", "un angusto", "un intricato", "un grande", "un meraviglioso", "uno strano", "un curioso"], 42 | walls: [ // dalle pareti 43 | "ruvide", 44 | "lisce", 45 | "taglienti", 46 | "porose", 47 | "irregolari", 48 | "marmoree", 49 | "punteggiate di minerali", 50 | ], 51 | ceeling: { 52 | types: [ // con un 53 | "basso", 54 | "ampio", 55 | "irregolare", 56 | ], 57 | ornaments: [ // soffitto ... 58 | "", 59 | "costellato di piccole stalattiti", 60 | "da dove scendono colonne di sedimenti", 61 | "di rocce affioranti", 62 | "coperto da fitte trame di ragnatele", 63 | "ricoperto da una folta vegetazione", 64 | "scuro, costellato di minuscoli cristalli" 65 | ] 66 | }, 67 | light: { 68 | sources: [ 69 | "Fioche torce incastonate nella pietra muovono ombre di qua e di là", 70 | "Il tenue blu-verde di minuscoli funghi bioluminescenti si diffonde opaco", 71 | "Impalpabili globi luminosi fluttuano pigramente", 72 | "Misteriose fiamme impalpabili si accendono e spengono", 73 | "Una serie di pozzi di fuoco, lingue degli inferi, illuminano l'ambiente", 74 | "Qua e la, maestosi cristalli brillano", 75 | "Alghe luminose si attaccano avide alle crepe illuminando l'aria" 76 | ], 77 | colors: [ 78 | "con un bagliore giallastro…", 79 | "con un bagliore caldo e mutevole…", 80 | "con una luce intensa…", 81 | "con una luce calda e rossastra…", 82 | "con una luce fredda…", 83 | "con una luce multicolore, allucinante…", 84 | "di una luce delicata…", 85 | "di una luce abbagliante…", 86 | "di una luce inquietante, tetra…", 87 | ] 88 | } 89 | }, 90 | gate_descriptions: { 91 | surrounding: [ 92 | "", 93 | "nel terreno", 94 | "tra i rovi", 95 | "tra spesse radici", 96 | "tra umidi massi", 97 | "tra le rocce", 98 | "tra rocce spoglie", 99 | "nella nuda roccia", 100 | ], 101 | dimensions: ["un", "uno stretto", "un agevole", "uno scomodo", "uno strano", "un intricato", "un curioso"], 102 | types: ["cunicolo", "passaggio", "continua il",], 103 | ornament: [ 104 | "", 105 | "avvolto in una fitta nebbia", 106 | "avvolto in una fitta vegetazione", 107 | "circondato di funghi rossi", 108 | "circondato di funghi gialli e viola", 109 | "vicino ad un piccolo corso d'acqua", 110 | ] 111 | }, 112 | 113 | 114 | 115 | }, 116 | 117 | 118 | room_noises: [ 119 | { 120 | main_noise: "\nAl centro:\nUn meraviglioso cenote incastonata nel cuore della roccia, l'acqua cristallina e trasparente riflette colori innaturali…", 121 | closed_rooms: "\nPlof! Plof!", 122 | further_rooms: "\nIl suono di piccole gocce che cadono riempie l'aria " 123 | }, 124 | { 125 | main_noise: "\nUn forte sibilo rimbalza tra le pietre echeggiando…", 126 | closed_rooms: "\nUn sibilo echeggia tra le pietre…", 127 | further_rooms: "\nAppena percettibile, un leggero sibilo echeggia nell'aria…" 128 | }, 129 | { 130 | main_noise: "\nIncessante, assordante, un battito ritmico e cupo, simile a quello di un cuore, batte.\nPulsa.", 131 | closed_rooms: "\n\"Tum. Tum. Tum!\"", 132 | further_rooms: "\nUn cupo battito si diffonde tra le rocce del suolo…" 133 | }, 134 | 135 | 136 | ], 137 | room_smells: [ 138 | { 139 | smell_source: "\nAl centro:\nUn grosso cumulo di scheletri ed ossa, ricoperto da un basso alone verde…", 140 | strong_smell: "\nNell'aria un aroma aspro e pungente…", 141 | light_smell: "\nNell'aria un odore acre…" 142 | }, 143 | { 144 | smell_source: "\nAl centro:\ni resti di un antico tempio…", 145 | strong_smell: "\nNell'aria un aroma pesante, ancestrale…", 146 | light_smell: "\nC'è odore di bruciato…" 147 | }, 148 | ], 149 | room_findings: [ 150 | { 151 | finding_source: "\nCRAAAK!\n(nrc says: «mi serve aiuto pe ste cose!»)", 152 | finding: "\nOvunque:\nnere piume…", 153 | } 154 | ], 155 | 156 | // DA qui si elimina... 157 | room_dimension_types: { micro: 0, small: 1, medium: 2, large: 3 }, 158 | room_types: { tunnel: 0, passage: 1, way: 2, special: 3, blind: 4, final: 5 }, // cunicolo, passaggio, corridoio, 159 | gate_types: { narrow: 0, standard: 1, large: "2" }, 160 | 161 | global_directions: ['nord', 'sud', 'est', 'ovest'], 162 | relative_direction: { 163 | nord: ['ovest', 'sud', 'est'], 164 | sud: ['est', 'nord', 'ovest'], 165 | est: ['nord', 'ovest', 'sud'], 166 | ovest: ['sud', 'est', 'nord'], 167 | }, 168 | 169 | 170 | gate_descriptions: { 171 | door: { 172 | name: "portone", 173 | attribute: ["un piccolo", "un imponente", "un massiccio", "un inquietante", "uno strano"], 174 | optional_attribute: ["in pietra ", "in legno ", "di ferro battuto ", ""], // condizionale 175 | material: ["corroso dal tempo", "nascosto tra una fenditura nella roccia", "lavorato con intagli intricati", "con strane incisioni", "decorato con motivi geometrici"] 176 | }, 177 | passage: { 178 | name: "passaggio", 179 | downhill: "in discesa", // in base al gate_type 180 | uphill: "in salita", // in base al gate_type 181 | optional_condition: ["", "nascosto tra le rocce ", "tra una fitta nebbia ", "tra una fitta vegetazione ", "tra rocce spoglie ", "nella nuda roccia ", "vicino ad una piccola sorgente "], 182 | condition: ["un", "un ampio", "un agevole", "uno strano", "un labirintico", "un intricato", "un misterioso"], 183 | optional_inside: ["dalle", "con", "avvolto da piante rampicanti e con", "ricoperto da vegetazione rigogliosa e con"], 184 | inside: ["pareti liscissime", "pareti ruvide", "pareti umide, viscide", "pareti irregolari e scagliose"] 185 | }, 186 | tunnel: { 187 | name: "cunicolo", 188 | attribute: ["un", "uno stretto", "un buio", "un angusto", "un intricato"], 189 | optional_attribute: ["infestato di rovi e radici", "tra umidi massi", "fangoso", "nel terreno", ""], 190 | optional_smell: ["avvolto nella penombra ", "immerso nell'oscurità ", "avvolto dal silenzio ", "coperto da ragnatele ", "", ""] 191 | } 192 | }, 193 | }, 194 | keyboard_buttons: { 195 | start_descent: { text: "(?) Salta…", callback_data: query_util.generate_callback_rute(sub_tree.altar.descent.stmp, sub_tree) }, 196 | face_left: { text: "◀️", callback_data: query_util.generate_callback_rute(sub_tree.maze.change_facing.stmp, sub_tree) }, 197 | face_right: { text: "▶️", callback_data: query_util.generate_callback_rute(sub_tree.maze.change_facing.stmp, sub_tree) }, 198 | face_back: { text: "🔽", callback_data: query_util.generate_callback_rute(sub_tree.maze.change_facing.stmp, sub_tree) }, 199 | gate_x: { text: "x", callback_data: query_util.generate_callback_rute(sub_tree.maze.goto_gate.stmp, sub_tree) } 200 | 201 | }, 202 | errors: { 203 | title: "Woops!", 204 | cant_load_instance: "Errore accedendo all'istanza…\nSe possibile contattare @nrc382", 205 | instance_locked: "␖\n\n(…)", 206 | cant_update_instance: "Errore aggiornando l'istanza…\nSe possibile contattare @nrc382", 207 | corrupted_instance: "La persistenza dell'istanza sembra sia corrotta…\nSe possibile contattare @nrc382", 208 | corrupted_segment: { 209 | nd_player: "ndplayer", 210 | nd_room: "ndroom", 211 | nd_gates: "ndgates", 212 | }, 213 | beta_wrong_input: "Sintassi: `/altarebeta` _?id\\\_utente ?id\\\_utente …_\n\nEsempio:\n> `/altarebeta 354140824 153738969`" 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /LootBot/views/strings.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Loot Game Bot - Telegram Bot 2 | 3 | Il primo gamebot di Telegram creato da Edoardo Cortese con più di 33.000 registrazioni disponibile dal 2016. 4 | 5 | Il bot utilizza il modulo "node-telegram-bot-api" per interagire con le Api di Telegram, un database MySql per memorizzare i dati e Node Js per essere eseguito. 6 | 7 | ## Codice 8 | 9 | Il codice è ora accessibile a tutti, questa decisione è stata presa con lo scopo di risolvere più velocemente i problemi o aggiungere funzionalità al bot in modo tale che chiunque possa contribuirne al miglioramento. 10 | 11 | Il codice è stato scritto ormai nel lontano 2016, quando il creatore era alle prime armi con Node Js, di conseguenza è logico trovare molte righe e metodi di lavoro non appropriati o non aggiornati. 12 | 13 | Per avviarlo nel proprio ambiente è necessario procedere secondo i seguenti passi: 14 | - Installare i pacchetti node con il comando npm install. 15 | - Il file config va rinominato rimuovendo la parte dopo il trattino basso, inserendo tutti i token dei bot necessari per quanto riguarda le funzionalità che si vogliono utilizzare. È per esempio possibile anche solo utilizzare quelli del bot principale (Loot Game Bot) e quelli del bot di supporto (Loot Plus Bot) ignorando gli altri. I dettagli sono presenti direttamente nel file, come commenti. 16 | - Dopo di che bisogna importare la struttura del database in un ambiente MySql. 17 | - Infine è necessario configurare nginx come proxy utilizzando il file di configurazione fornito, aggiungendo un certificato valido ed una chiave. 18 | - È consigliato utilizzare un gestore di processi come PM2 per far partire i vari bot. 19 | 20 | È presente anche la LootBotApi accessibile via web nella [relativa repository](https://github.com/sidelux/LootBotApi). 21 | Per installarla è necessario anche in questo caso rinominare e modificare il file config.js, specificare validi certificati ssl e poi avviarlo come gli altri script. 22 | 23 | È bene tenere in conto che il bot non è mai stato testato in questo stato con 0 giocatori registrati, potrebbe avere comportamenti inattesi. 24 | 25 | ## Contributi 26 | 27 | Per contribuire è possibile creare PR direttamente tramite Github oppure contattare direttamente @fenix45 su Telegram, seguendo queste indicazioni: 28 | - Esegui il fork del progetto 29 | - Crea il tuo branch con un nome parlante 30 | - Esegui il commit delle modifiche 31 | - Esegui push 32 | - Apri una Pull Request dettagliando le modifiche effettuate 33 | 34 | Prima di creare una richiesta è bene verificare che il codice sia funzionante e valido e si riesca a innestare bene nel bilanciamento del gioco. 35 | 36 | ## TODO 37 | 38 | Ci sono vari interventi che ho pensato di apportare ma per mancanza di tempo o risorse non sono mai riuscito ad avviare: 39 | - [x] Predisposizione per l'open source 40 | - [x] Automazione completa della gestione degli eventi e della globale 41 | - [ ] Divisione del codice in moduli/file (in corso) 42 | - [ ] Aggiunta periodica delle nuove funzionalità sulla base dei suggerimenti della community tramite i contributi degli altri sviluppatori 43 | - [ ] Bilanciamento generale che nel tempo ha portato ad inflazione e rottura di molte meccaniche del gioco (in corso) 44 | - [ ] Aggiunta del supporto al multi lingua (principalmente l'inglese) 45 | 46 | I contributi a questi interventi avranno maggior priorità rispetto alle altre modifiche. 47 | 48 | ## Issue 49 | 50 | Per segnalare eventuali problemi al codice non è necessario per forza modificare il codice, è anche possibile creare una richiesta Issue rispettando l'apposito template per indicare con più dettagli possibili quali errori si riscontrano. 51 | Io o qualcun'altro provvederemo a risolvere il problema o commentare con maggiori indicazioni. 52 | 53 | ## Autori 54 | 55 | * **Edoardo Cortese** - *Programmazione* - [fenix45](http://telegram.me/fenix45) 56 | * **Emanuele Finiguerra** - *Analisi e bilanciamenti* - [LastSoldier95](http://telegram.me/LastSoldier95) 57 | * **Dario Emerson** - Fix e migliorie strutturali 58 | * **@Delooo** - Mob generator 59 | 60 | ## Licenza 61 | 62 | Questo progetto è sotto licenza GNU AGPLv3 - leggi la [documentazione](https://choosealicense.com/licenses/agpl-3.0/) per ulteriori dettagli. 63 | -------------------------------------------------------------------------------- /config.js_default: -------------------------------------------------------------------------------- 1 | var config = {}; 2 | 3 | config.server = ""; // server web in entrata (es. https://fenixweb.net:8443) 4 | config.dbhost = ""; // host database 5 | 6 | config.dbuser = ""; // utente database 7 | config.dbpassword = ""; // password database 8 | config.dbdatabase = ""; // nome database 9 | 10 | config.maintoken = ""; // token loot 11 | config.plustoken = ""; // token plus 12 | config.cltoken = ""; // token craftlootbot (non utilizzato) 13 | config.shorttoken = ""; // token shorte.st (https://shorte.st/) 14 | config.shortpetoken = ""; // token short.pe (https://short.pe/) 15 | config.gendertoken = ""; // token gender api (https://gender-api.com/en/) 16 | 17 | config.creatore_id = 0; // id creatore (non utilizzato o comunque uguale a phoenix id) 18 | config.phenix_id = 0; // id amministratore 19 | 20 | config.sugg_antifloodTime = 1, // non utilizzato 21 | config.LootSuggChannel = "", // non utilizzato 22 | config.LootAvvisiChannel = "", // non utilizzato 23 | 24 | config.databaseLootUser = "", // non utilizzato 25 | config.databaseHost = "", // non utilizzato 26 | config.databasePsw = "", // non utilizzato 27 | config.databaseSuggName = "", // non utilizzato 28 | 29 | config.tips_enabled = true; // non utilizzato 30 | config.manual_log = false, // non utilizzato 31 | config.simple_log = false, // non utilizzato 32 | 33 | module.exports = config; 34 | -------------------------------------------------------------------------------- /events.ini_default: -------------------------------------------------------------------------------- 1 | crazyMode=0 2 | luckyMode=0 3 | arena=0 4 | lootteria=0 5 | villa=0 6 | wanted=0 7 | eventTeamStory=0 8 | eventFestival=0 9 | specialMission=0 10 | checkDragonTopOn=0 11 | gnomorra=0 12 | dungeonRush=0 13 | -------------------------------------------------------------------------------- /lb_problem.js: -------------------------------------------------------------------------------- 1 | process.on('uncaughtException', function (err) { 2 | console.error(err); 3 | }); 4 | 5 | var TelegramBot = require('node-telegram-bot-api'); 6 | var config = require('./config.js'); 7 | var token = config.maintoken; 8 | var bot = new TelegramBot(token, {polling: true}); 9 | 10 | bot.on('message', function (message) { 11 | console.log(message.from.username + ": " + message.text); 12 | 13 | var text = "Manutenzione, riprova tra poco. Segui @LootBotAvvisi per aggiornamenti."; 14 | 15 | bot.sendMessage(message.chat.id, text); 16 | }); 17 | -------------------------------------------------------------------------------- /lbp_problem.js: -------------------------------------------------------------------------------- 1 | process.on('uncaughtException', function (err) { 2 | console.error(err); 3 | }); 4 | 5 | var TelegramBot = require('node-telegram-bot-api'); 6 | var config = require('./config.js'); 7 | var token = config.plustoken; 8 | var bot = new TelegramBot(token, {polling: true}); 9 | 10 | bot.on('message', function (message) { 11 | console.log("Manutenzione: " + message.from.username + ": " + message.text); 12 | }); 13 | -------------------------------------------------------------------------------- /loot-nginx-conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 8443 default_server; 3 | listen [::]:8443 default_server; 4 | 5 | ssl on; 6 | ssl_certificate /etc/ssl/fenixweb/fenixweb_nginx.crt; 7 | ssl_certificate_key /etc/ssl/fenixweb/fenixweb.key; 8 | 9 | root /var/www/html; 10 | 11 | index index.html index.htm index.nginx-debian.html; 12 | 13 | server_name _; 14 | 15 | location / { 16 | try_files $uri $uri/ =404; 17 | } 18 | 19 | location /loot { 20 | proxy_pass http://localhost:25001; 21 | } 22 | 23 | location /plus { 24 | proxy_pass http://localhost:25002; 25 | } 26 | } -------------------------------------------------------------------------------- /mobGenerator.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Mob Generator by @Delooo 4 | 5 | var mobGenerator = require('./mobGenerator.js'); 6 | mobGenerator.generate(); 7 | 8 | */ 9 | 10 | LanguageRules = { 11 | code: "it", 12 | verbsGoAfterNoun: false, 13 | useLowercaseNouns: false, 14 | useLowercaseVerbs: false, 15 | doubleIntervocalicRs: false, 16 | doubleIntervocalicSs: false, 17 | hyphenateCompoundNouns: false, 18 | useUppercaseAfterHyphens: false, 19 | hyphenateCompoundNounsWithDoubleLetters: true, 20 | dontUseBasePartsAsAdjectives: false, 21 | addArticlesToNames: false, 22 | removeAllSpaces: false, 23 | ignoreParentheses: false 24 | }; 25 | 26 | complexity = 1.0; 27 | activeGender = 0; 28 | activeAdjectivePlacement = 0; 29 | 30 | CurrentLexiconLists = { 31 | Adjectives_beforeNoun_gender1: ["Gigantesco", "Piccolo", "Bavoso", "Fetido"], 32 | Adjectives_afterNoun_gender1: [ 33 | "della foresta", 34 | "delle terre dei Grumpi", 35 | "delle grandi vallate", 36 | "del popolo del vulcano", 37 | "dai confini del mondo", 38 | "affamato", 39 | "arrabbiato", 40 | "furioso", 41 | "docile", 42 | "in preda al panico", 43 | "armato", 44 | "disarmato", 45 | "zombie", 46 | "impietrito", 47 | "logorato", 48 | "sfregiato", 49 | "stanco", 50 | "esausto", 51 | "assassino", 52 | "divertito", 53 | "aggressivo", 54 | "di metallo", 55 | "di fuoco", 56 | "verde", 57 | "rosso", 58 | "blu", 59 | "confuso", 60 | "contorto", 61 | "mutante", 62 | "simpatico", 63 | "suicida", 64 | "malintenzionato", 65 | "possente", 66 | "appiccicoso", 67 | "fantasmagorico", 68 | "barbuto", 69 | "belga", 70 | "bendato", 71 | "bulboso", 72 | "Edoriano", 73 | "esplosivo", 74 | "dal fiato letale", 75 | "pazzo", 76 | "giovane", 77 | "importante", 78 | "cinguettante", 79 | "contaminato", 80 | "criptico", 81 | "debole", 82 | "del potere", 83 | "sdentato", 84 | "di Lootia", 85 | "di Efesto", 86 | "di Zeus", 87 | "di Poseidone", 88 | "eccentrico", 89 | "ecologico", 90 | "etereo", 91 | "extralarge", 92 | "flessibile", 93 | "forte", 94 | "geneticamente modificato", 95 | "improbabile", 96 | "ingrassato", 97 | "lunare", 98 | "maggiore", 99 | "magnetico", 100 | "meccanico", 101 | "medio", 102 | "mercuriale", 103 | "miniaturizzato", 104 | "minore", 105 | "modificato", 106 | "nervoso", 107 | "novizio", 108 | "oscuro", 109 | "omicida", 110 | "paranormale", 111 | "peculiare", 112 | "perplesso", 113 | "pirotecnico", 114 | "potenziato", 115 | "radioattivo", 116 | "raffreddato", 117 | "rivoluzionario", 118 | "rotante", 119 | "rotolante", 120 | "rumoroso", 121 | "robotico", 122 | "sbadato", 123 | "silenzioso", 124 | "sonico", 125 | "spadaccino", 126 | "spaziale", 127 | "stellare", 128 | "striato", 129 | "sudato", 130 | "trasparente", 131 | "volante", 132 | "violento", 133 | "zoppicante", 134 | "quadridimensionale" 135 | ], 136 | Adjectives_beforeNoun_gender2: ["Piccola", "Grande", "Strana", "Incantevole"], 137 | Adjectives_afterNoun_gender2: [ 138 | "della foresta", 139 | "delle terre dei Grumpi", 140 | "delle grandi vallate", 141 | "del popolo del vulcano", 142 | "delle vallate impervie", 143 | "affamata", 144 | "arrabbiata", 145 | "furiosa", 146 | "docile", 147 | "in preda al panico", 148 | "armata", 149 | "disarmata", 150 | "zombie", 151 | "impietrita", 152 | "logorata", 153 | "sfregiata", 154 | "stanca", 155 | "esausta", 156 | "assassina", 157 | "divertita", 158 | "aggressiva", 159 | "di metallo", 160 | "di fuoco", 161 | "verde", 162 | "rossa", 163 | "blu", 164 | "confusa", 165 | "contorta", 166 | "mutante", 167 | "simpatica", 168 | "suicida", 169 | "malintenzionata", 170 | "possente", 171 | "appiccicosa", 172 | "fantasmagorica", 173 | "barbuta", 174 | "belga", 175 | "bendata", 176 | "bulbosa", 177 | "Edoriana", 178 | "esplosiva", 179 | "dal fiato letale", 180 | "pazza", 181 | "importante", 182 | "cinguettante", 183 | "contaminata", 184 | "criptica", 185 | "debole", 186 | "del potere", 187 | "sdentata", 188 | "di Lootia", 189 | "di Efesto", 190 | "di Zeus", 191 | "di Poseidone", 192 | "eccentrica", 193 | "ecologica", 194 | "eterea", 195 | "extralarge", 196 | "flessibile", 197 | "forte", 198 | "geneticamente modificata", 199 | "improbabile", 200 | "ingrassata", 201 | "lunare", 202 | "maggiore", 203 | "magnetica", 204 | "meccanica", 205 | "media", 206 | "mercuriale", 207 | "miniaturizzata", 208 | "minore", 209 | "modificata", 210 | "nervosa", 211 | "novizia", 212 | "oscura", 213 | "omicida", 214 | "paranormale", 215 | "peculiare", 216 | "perplessa", 217 | "pirotecnica", 218 | "potenziata", 219 | "radioattiva", 220 | "raffreddata", 221 | "rivoluzionaria", 222 | "rotante", 223 | "rotolante", 224 | "rumorosa", 225 | "robotica", 226 | "sbadata", 227 | "silenziosa", 228 | "sonica", 229 | "spaziale", 230 | "stellare", 231 | "striata", 232 | "trasparente", 233 | "volante", 234 | "violenta", 235 | "zoppicante", 236 | "quadridimensionale" 237 | ], 238 | Adjectives_beforeNoun_gender3: [], 239 | Adjectives_afterNoun_gender3: [], 240 | Nouns_gender1: [ 241 | "nano", 242 | "orco", 243 | "scheletro", 244 | "mostro", 245 | "bardo", 246 | "demone", 247 | "troll", 248 | "goblin", 249 | "elfo", 250 | "gigante", 251 | "guerriero", 252 | "golem", 253 | "drago", 254 | "falciatore", 255 | "arciere", 256 | "soldato", 257 | "cannoniere", 258 | "artigliere", 259 | "distruttore", 260 | "frantumateste", 261 | "divoratore", 262 | "giullare", 263 | "pazzoide", 264 | "ninja", 265 | "serpente", 266 | "essere", 267 | "cavaliere", 268 | "barbaro", 269 | "centauro", 270 | "mercenario", 271 | "lanciere", 272 | "lottatore", 273 | "evocatore", 274 | "licantropo", 275 | "gargoyle", 276 | "zombie", 277 | "mago", 278 | "stregone", 279 | "mech", 280 | "martellatore", 281 | "tiratore", 282 | "caposcout" 283 | ], 284 | Nouns_gender2: [ 285 | "valchiria", 286 | "viverna", 287 | "guardia", 288 | "mangiauomini", 289 | "pianta carnivora", 290 | "ubriacona", 291 | "combattente", 292 | "vichinga", 293 | "strega", 294 | "maga", 295 | "sferragliatrice", 296 | "macellaia", 297 | "sacerdotessa", 298 | "ribelle", 299 | "bestia" 300 | ], 301 | Nouns_gender3: [], 302 | CompoundAdjectives_prefix: [], 303 | CompoundAdjectives_base_beforeNoun_gender1: [], 304 | CompoundAdjectives_base_afterNoun_gender1: [], 305 | CompoundAdjectives_base_beforeNoun_gender2: [], 306 | CompoundAdjectives_base_afterNoun_gender2: [], 307 | CompoundAdjectives_base_beforeNoun_gender3: [], 308 | CompoundAdjectives_base_afterNoun_gender3: [], 309 | CompoundNouns_prefix: [], 310 | CompoundNouns_base_gender1: [], 311 | CompoundNouns_base_gender2: [], 312 | CompoundNouns_base_gender3: [] 313 | }; 314 | 315 | function setCurrentLexiconLists(lists) { 316 | CurrentLexiconLists = lists; //CurrentLexiconLists è un oggetto i cui attributi sono categorie ovvero array di parole 317 | } 318 | 319 | function getWordList(wordListId) { 320 | var wordList = CurrentLexiconLists[wordListId]; 321 | return wordList; 322 | } 323 | 324 | function generateMobCompleteName() { 325 | var name; 326 | var x = getRandomInt(0, 3); 327 | if (x == 0) { 328 | name = GetShortRandomName(); 329 | } else if (x == 1) { 330 | name = GetMediumRandomName(); 331 | } else if (x == 2) { 332 | name = GetLongRandomName(); 333 | } 334 | 335 | return name; 336 | } 337 | 338 | function isValidWordList(list) { 339 | return list && list.length > 0 && list[0].length > 0; 340 | } 341 | 342 | function hasCompoundNouns() { 343 | return false; 344 | } 345 | 346 | function hasCompoundAdjectives() { 347 | return false; 348 | } 349 | 350 | function GetRandomNoun() { 351 | var base; 352 | base = GetRandomNounWord(); 353 | return base; 354 | } 355 | 356 | function GetRandomLocalizedWord(genderLists, cmplx) { 357 | if (activeGender == 0) { 358 | var totalWords = countOfAllLists(genderLists); 359 | var n = getRandomInt(0, totalWords); 360 | activeGender = listIndexOfItemIndex(genderLists, n) + 1; 361 | } 362 | 363 | return GetRandomLocalizedWordForGender(genderLists, activeGender, cmplx); 364 | } 365 | 366 | function GetRandomLocalizedWordForGender(genderLists, gender, cmplx) { 367 | if (genderLists.length <= 3) { 368 | if (isValidWordList(genderLists[gender - 1])) 369 | return randomWordFrom(genderLists[gender - 1], cmplx); 370 | else { 371 | // If there's no gender-specialization for this list, use the default gender 372 | if (isValidWordList(genderLists[0])) { 373 | return GetRandomLocalizedWordForGender(genderLists, 1, cmplx); 374 | } else { 375 | return ""; 376 | } 377 | } 378 | } else { 379 | gender -= gender > 3 ? 3 : 0; 380 | var genderSpecificLists = []; 381 | if ( 382 | isValidWordList(genderLists[gender - 1]) || 383 | isValidWordList(genderLists[gender + 3 - 1]) 384 | ) { 385 | genderSpecificLists.push(genderLists[gender - 1]); 386 | genderSpecificLists.push(genderLists[gender + 3 - 1]); 387 | 388 | var totalGenderSpecificWords = countOfAllLists(genderSpecificLists); 389 | var n = getRandomInt(0, totalGenderSpecificWords); 390 | activeAdjectivePlacement = listIndexOfItemIndex(genderSpecificLists, n); 391 | 392 | return randomWordFrom( 393 | genderSpecificLists[activeAdjectivePlacement], 394 | cmplx 395 | ); 396 | } else { 397 | // If there's no gender-specialization for this list, use the default gender 398 | if (isValidWordList(genderLists[0])) 399 | return GetRandomLocalizedWordForGender(genderLists, 1, cmplx); 400 | else return ""; 401 | } 402 | } 403 | } 404 | 405 | function GetRandomAdjective() { 406 | var adjective = new Object(); 407 | adjective.word = ""; 408 | adjective.comesBeforeNoun = true; 409 | 410 | adjective.word = GetRandomLocalizedWord( 411 | [ 412 | getWordList("Adjectives_beforeNoun_gender1"), 413 | getWordList("Adjectives_beforeNoun_gender2"), 414 | getWordList("Adjectives_beforeNoun_gender3"), 415 | getWordList("Adjectives_afterNoun_gender1"), 416 | getWordList("Adjectives_afterNoun_gender2"), 417 | getWordList("Adjectives_afterNoun_gender3") 418 | ], 419 | complexity 420 | ); 421 | 422 | if (activeAdjectivePlacement == 1) { 423 | adjective.comesBeforeNoun = false; 424 | if (activeGender > 3) { 425 | activeGender -= 3; 426 | } 427 | } 428 | return adjective; 429 | } 430 | 431 | function GetRandomNounWord() { 432 | return GetRandomLocalizedWord( 433 | [ 434 | getWordList("Nouns_gender1"), 435 | getWordList("Nouns_gender2"), 436 | getWordList("Nouns_gender3") 437 | ], 438 | complexity 439 | ); 440 | } 441 | 442 | function GetShortRandomName() { 443 | return GetMediumRandomName(); 444 | } 445 | 446 | function GetMediumRandomName() { 447 | var name; 448 | var noun = GetRandomNoun(); 449 | var adjective = GetRandomAdjective(); 450 | 451 | var firstPart = adjective.comesBeforeNoun ? adjective.word : noun; 452 | var lastPart = adjective.comesBeforeNoun ? noun : adjective.word; 453 | 454 | if (firstPart.length == 0) name = upperCaseFirstLetter(lastPart); 455 | else if (lastPart.length == 0) name = upperCaseFirstLetter(firstPart); 456 | else { 457 | var separator = " "; 458 | name = upperCaseFirstLetter(firstPart) + separator + lastPart; 459 | } 460 | 461 | return name; 462 | } 463 | 464 | function GetLongRandomName() { 465 | var name; 466 | var adjective = GetRandomAdjective(); 467 | var noun = GetRandomNoun(); 468 | 469 | var firstPart = adjective.comesBeforeNoun ? adjective.word : noun; 470 | var lastPart = adjective.comesBeforeNoun ? noun : adjective.word; 471 | 472 | if (firstPart.length == 0) name = upperCaseFirstLetter(lastPart); 473 | else if (lastPart.length == 0) name = upperCaseFirstLetter(firstPart); 474 | else { 475 | var separator = " "; 476 | name = upperCaseFirstLetter(firstPart) + separator + lastPart; 477 | } 478 | 479 | return name; 480 | } 481 | 482 | function getRandomInt(min, max) { 483 | return Math.floor(Math.random() * (max - min)) + min; 484 | } 485 | 486 | function randomIndexFrom(wordList, cmplx) { 487 | var a = wordList.length * 0.5; 488 | var b = wordList.length; 489 | var upperBound = Math.floor(a + cmplx * (b - a)); 490 | var wordIndex = getRandomInt(0, upperBound); 491 | return wordIndex; 492 | } 493 | 494 | function randomWordFrom(wordList, cmplx) { 495 | if (cmplx == undefined) cmplx = 1; 496 | var n = randomIndexFrom(wordList, cmplx); 497 | var word = wordList[n]; 498 | return word; 499 | } 500 | 501 | function processLanguageRules(name) { 502 | return removeParenthesizedWords(name); 503 | } 504 | 505 | function removeParenthesizedWords(text) { 506 | var openParenIndex = text.indexOf("("); 507 | var closeParenIndex = text.indexOf(")"); 508 | 509 | if (openParenIndex != -1 && closeParenIndex != -1) { 510 | var removalEndIndex = closeParenIndex; 511 | var nextChar = text[closeParenIndex + 1]; 512 | if (nextChar == " ") removalEndIndex += 1; // Also remove the space 513 | return text.slice(0, openParenIndex) + text.slice(removalEndIndex + 1); 514 | } else return text; 515 | } 516 | 517 | function sortByStringLength(a, b) { 518 | if (a.length < b.length) return -1; 519 | else if (a.length == b.length) return 0; 520 | else if (a.length > b.length) return 1; 521 | } 522 | 523 | function lowerCaseFirstLetter(word) { 524 | return word[0].toLowerCase() + word.substring(1); 525 | } 526 | 527 | function upperCaseFirstLetter(word) { 528 | return word[0].toUpperCase() + word.substring(1); 529 | } 530 | 531 | function isVowel(c) { 532 | var vowels = "aeiou"; 533 | return vowels.indexOf(c) != -1; 534 | } 535 | 536 | function countOfAllLists(lists) { 537 | var total = 0; 538 | for (i = 0; i < lists.length; ++i) total += lists[i].length; 539 | return total; 540 | } 541 | 542 | function listIndexOfItemIndex(lists, n) { 543 | var listIndex = -1; 544 | for (var i = 0; i < lists.length; i++) { 545 | if (n < lists[i].length) { 546 | listIndex = i; 547 | break; 548 | } else n -= lists[i].length; 549 | } 550 | 551 | return listIndex; 552 | } 553 | 554 | function firstLetterOf(s) { 555 | return s[0]; 556 | } 557 | 558 | function lastLetterOf(s) { 559 | return s[s.length - 1]; 560 | } 561 | 562 | function reportError() { 563 | var breakHere = true; 564 | } 565 | 566 | exports.generate = function() { 567 | return generateMobCompleteName(); 568 | }; 569 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "loot", 3 | "version": "1.0.0", 4 | "description": "Telegram game bot by Edoardo Cortese", 5 | "main": "lootBot.js", 6 | "dependencies": { 7 | "body-parser": "^1.20.2", 8 | "express": "^4.18.2", 9 | "fs": "0.0.2", 10 | "got": "^11.8.3", 11 | "http": "0.0.1-security", 12 | "https": "^1.0.0", 13 | "ini-builder": "^1.1.1", 14 | "isomorphic-fetch": "^3.0.0", 15 | "mathjs": "^11.7.0", 16 | "moment": "^2.29.4", 17 | "morgan": "^1.10.0", 18 | "ms": "^2.1.3", 19 | "mysql": "^2.18.1", 20 | "node-fetch": "^3.3.1", 21 | "node-schedule": "^2.1.1", 22 | "node-telegram-bot-api": "^0.61.0", 23 | "nodejs-captcha": "^0.0.6", 24 | "npm-check-updates": "^16.7.13", 25 | "pdfkit": "^0.13.0", 26 | "readline": "^1.3.0", 27 | "request": "^2.88.2", 28 | "string-similarity": "^4.0.4", 29 | "sync-mysql": "^3.0.1" 30 | }, 31 | "devDependencies": { 32 | "eslint": "^8.36.0", 33 | "eslint-config-standard": "^17.0.0", 34 | "eslint-plugin-import": "^2.27.5", 35 | "eslint-plugin-node": "^11.1.0", 36 | "eslint-plugin-promise": "^6.1.1" 37 | }, 38 | "scripts": { 39 | "test": "echo \"Error: no test specified\" && exit 1" 40 | }, 41 | "author": "Edoardo Cortese", 42 | "license": "GPL-3.0", 43 | "repository": { 44 | "type": "git", 45 | "url": "git+https://sidelux@bitbucket.org/sidelux/lootbot.git" 46 | }, 47 | "homepage": "https://bitbucket.org/sidelux/lootbot#readme" 48 | } 49 | -------------------------------------------------------------------------------- /pdfkit-tables.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const PDFDocument = require('pdfkit'); 4 | 5 | class PDFDocumentWithTables extends PDFDocument { 6 | constructor (options) { 7 | super(options); 8 | } 9 | 10 | table (table, arg0, arg1, arg2) { 11 | let startX = this.page.margins.left, startY = this.y; 12 | let options = {}; 13 | 14 | if ((typeof arg0 === 'number') && (typeof arg1 === 'number')) { 15 | startX = arg0; 16 | startY = arg1; 17 | 18 | if (typeof arg2 === 'object') 19 | options = arg2; 20 | } else if (typeof arg0 === 'object') { 21 | options = arg0; 22 | } 23 | 24 | const columnCount = table.headers.length; 25 | const columnSpacing = options.columnSpacing || 15; 26 | const rowSpacing = options.rowSpacing || 5; 27 | const usableWidth = options.width || (this.page.width - this.page.margins.left - this.page.margins.right); 28 | 29 | const prepareHeader = options.prepareHeader || (() => {}); 30 | const prepareRow = options.prepareRow || (() => {}); 31 | const computeRowHeight = (row) => { 32 | let result = 0; 33 | 34 | row.forEach((cell) => { 35 | const cellHeight = this.heightOfString(cell, { 36 | width: columnWidth, 37 | align: 'left' 38 | }); 39 | result = Math.max(result, cellHeight); 40 | }); 41 | 42 | return result + rowSpacing; 43 | }; 44 | 45 | const columnContainerWidth = usableWidth / columnCount; 46 | const columnWidth = columnContainerWidth - columnSpacing; 47 | const maxY = this.page.height - this.page.margins.bottom; 48 | 49 | let rowBottomY = 0; 50 | 51 | this.on('pageAdded', () => { 52 | startY = this.page.margins.top; 53 | rowBottomY = 0; 54 | }); 55 | 56 | // Allow the user to override style for headers 57 | prepareHeader(); 58 | 59 | // Check to have enough room for header and first rows 60 | if (startY + 3 * computeRowHeight(table.headers) > maxY) 61 | this.addPage(); 62 | 63 | // Print all headers 64 | table.headers.forEach((header, i) => { 65 | this.text(header, startX + i * columnContainerWidth, startY, { 66 | width: columnWidth, 67 | align: 'left' 68 | }); 69 | }); 70 | 71 | // Refresh the y coordinate of the bottom of the headers row 72 | rowBottomY = Math.max(startY + computeRowHeight(table.headers), rowBottomY); 73 | 74 | // Separation line between headers and rows 75 | this.moveTo(startX, rowBottomY - rowSpacing * 0.5) 76 | .lineTo(startX + usableWidth, rowBottomY - rowSpacing * 0.5) 77 | .lineWidth(2) 78 | .stroke(); 79 | 80 | table.rows.forEach((row, i) => { 81 | const rowHeight = computeRowHeight(row); 82 | 83 | // Switch to next page if we cannot go any further because the space is over. 84 | // For safety, consider 3 rows margin instead of just one 85 | if (startY + 3 * rowHeight < maxY) 86 | startY = rowBottomY + rowSpacing; 87 | else 88 | this.addPage(); 89 | 90 | // Allow the user to override style for rows 91 | prepareRow(row, i); 92 | 93 | // Print all cells of the current row 94 | row.forEach((cell, i) => { 95 | this.text(cell, startX + i * columnContainerWidth, startY, { 96 | width: columnWidth, 97 | align: 'left' 98 | }); 99 | }); 100 | 101 | // Refresh the y coordinate of the bottom of this row 102 | rowBottomY = Math.max(startY + rowHeight, rowBottomY); 103 | 104 | // Separation line between rows 105 | this.moveTo(startX, rowBottomY - rowSpacing * 0.5) 106 | .lineTo(startX + usableWidth, rowBottomY - rowSpacing * 0.5) 107 | .lineWidth(1) 108 | .opacity(0.7) 109 | .stroke() 110 | .opacity(1); // Reset opacity after drawing the line 111 | }); 112 | 113 | this.x = startX; 114 | this.moveDown(); 115 | 116 | return this; 117 | } 118 | } 119 | 120 | module.exports = PDFDocumentWithTables; -------------------------------------------------------------------------------- /suggestions/models/SuggestionsTables/allTables_struct.json: -------------------------------------------------------------------------------- 1 | { 2 | "sugg": " (SUGGESTION_ID VARCHAR(8) NOT NULL PRIMARY KEY, MSG_ID int NOT NULL, SUSER_ID int(8) NOT NULL, SDATE VARCHAR(13) NOT NULL, STEXT TEXT NOT NULL, SCLOSED int(2) NOT NULL DEFAULT 0, SONCLOSE_UPVOTE int(8) NOT NULL DEFAULT 0, SONCLOSE_DOWNVOTE int(8) NOT NULL DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;", 3 | "usr": " (USER_ID int(8) unsigned NOT NULL PRIMARY KEY, DEV_NICK VARCHAR(10) DEFAULT NULL, LAST_CHECK VARCHAR(13) NOT NULL DEFAULT '0', USER_ROLE int(1) NOT NULL DEFAULT 1, USER_LASTMESS VARCHAR(13) NOT NULL DEFAULT '0', USER_LASTSUGG VARCHAR(13) NOT NULL DEFAULT '0', USER_LASTQUERYDATE VARCHAR(13) NOT NULL DEFAULT '0', WARN int(4) NOT NULL DEFAULT 0,USER_TMP_MSG TEXT NOT NULL DEFAULT '' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;", 4 | "votes": " (USER_ID int(8) unsigned NOT NULL, AUTHOR_ID int(8) NOT NULL DEFAULT 0, SUGGESTION_ID VARCHAR(8) NOT NULL,SUVOTE int(4) NOT NULL,PRIMARY KEY (SUGGESTION_ID,USER_ID)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;", 5 | "banditw": "(BW_POS int(6) unsigned NOT NULL PRIMARY KEY, B_WORD VARCHAR(26) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;" 6 | } -------------------------------------------------------------------------------- /suggestions/models/bandit_w.txt: -------------------------------------------------------------------------------- 1 | affanculo 2 | bagasc 3 | baldracc 4 | bastard 5 | madonna 6 | batton 7 | bocchin 8 | Dio 9 | dio 10 | piscio 11 | cacare 12 | cacasotto 13 | caga 14 | allah 15 | cazz 16 | checc 17 | chiavar 18 | chiavat 19 | ciuccia 20 | coglion 21 | coprof 22 | cornuto 23 | cretin 24 | cristo 25 | culatton 26 | culo 27 | deficient 28 | cojon 29 | fanculo 30 | fichett 31 | ficona 32 | ficone 33 | figa 34 | figh 35 | troia 36 | fottere 37 | fottiti 38 | fottut 39 | fregna 40 | frocetto 41 | froci 42 | gesù 43 | incazza 44 | incula 45 | leccap 46 | leccac 47 | leccaf 48 | lurida 49 | mongoloide 50 | mamma 51 | merd 52 | merdin 53 | merdolin 54 | merdon 55 | merdos 56 | mezzaseg 57 | mignott 58 | minchi 59 | mona 60 | negra 61 | negraccia 62 | negraccio 63 | negro 64 | negrona 65 | negrone 66 | nerchia 67 | patonz 68 | pisello 69 | piscia 70 | pompin 71 | porco 72 | clero 73 | pugnett 74 | puttan 75 | raspon 76 | ricchion 77 | sborra 78 | sburra 79 | sburrare 80 | stronz 81 | succhia 82 | tarzanell 83 | tetta 84 | tette 85 | tettina 86 | tettine 87 | tettona 88 | tettone 89 | troia 90 | troie 91 | tromba 92 | vacca 93 | vaffa 94 | zinne 95 | zoccola 96 | scopare 97 | scopata 98 | scopate -------------------------------------------------------------------------------- /suggestions/tips_utils.js: -------------------------------------------------------------------------------- 1 | let telegram_stat = { 2 | messages: 0, 3 | sent_msg: 0, 4 | callBack: 0, 5 | inline: 0, 6 | errori: 0 7 | }; 8 | 9 | function chunkSubstr(str, size) { // il text_msg e una lunghezza *limite* 10 | let str_array = str.split("\n"); 11 | let my_len = str_array.length; 12 | const numChunks = Math.ceil(my_len / size); // console.log("> Saranno " + numChunks + " messaggi\n\n"); 13 | const chunks = new Array(numChunks); 14 | let str_copy = str_array.slice(); 15 | 16 | let mile_stone = 0; 17 | let counter = 0; 18 | for (let i = 0; i < my_len; i++) { 19 | mile_stone++; 20 | if (mile_stone >= size || (mile_stone > size / 2 && (str_copy[i].length == 0 || str_copy[i] == "\n"))) { 21 | chunks[counter] = str_array.slice(0, mile_stone).join("\n"); 22 | str_array = str_array.slice(mile_stone); 23 | counter++; 24 | mile_stone = 0; 25 | } 26 | } 27 | 28 | 29 | return (chunks); //L'array finale. Ogni elemento contiene il testo_parziale (un messaggio) 30 | } 31 | 32 | 33 | function bigSend(res_mess, istanza_bot) { 34 | if (typeof (res_mess) != "undefined") { 35 | let res_array = []; 36 | if (!(res_mess instanceof Array)) { 37 | res_array.push(res_mess); 38 | } else { 39 | res_array = res_mess.slice(0, res_mess.length); 40 | } 41 | 42 | 43 | for (let i = 0; i < res_array.length; i++) { 44 | 45 | 46 | // TO DELETE 47 | if (typeof (res_array[i].toDelete) != "undefined") { 48 | istanza_bot.deleteMessage( 49 | res_array[i].toDelete.chat_id, 50 | res_array[i].toDelete.mess_id 51 | ).catch(function (err) { 52 | telegram_stat.errori++; 53 | console.log("!toDelete -> "); 54 | console.log(err.response.body); 55 | }); 56 | } 57 | 58 | // TO SEND 59 | if (typeof (res_array[i].toSend) != "undefined") { 60 | let to_check; 61 | 62 | 63 | if (typeof res_array[i].toSend.message_text != "undefined") { 64 | to_check = res_array[i].toSend.message_text; 65 | } else { 66 | to_check = res_array[i].toSend.message_txt; 67 | } 68 | 69 | if (to_check.length >= 3500) { 70 | console.log("> Ho un testo da dividere!") 71 | let arr = chunkSubstr(to_check, 100); 72 | for (let l = 0; l < arr.length; l++) { 73 | telegram_stat.sent_msg++; 74 | istanza_bot.sendMessage( 75 | res_array[i].toSend.chat_id, 76 | arr[l], 77 | res_array[i].toSend.options 78 | ).catch(function (err) { 79 | telegram_stat.errori++; 80 | istanza_bot.sendMessage( 81 | res_array[i].toSend.chat_id, 82 | parseError_parser(err, arr[l]) 83 | ).catch(function (err2) { 84 | telegram_stat.errori++; 85 | 86 | console.log(err2) 87 | }); 88 | }); 89 | } 90 | } else { 91 | telegram_stat.sent_msg++; 92 | istanza_bot.sendMessage( 93 | res_array[i].toSend.chat_id, 94 | to_check, 95 | res_array[i].toSend.options 96 | ).catch(function (err) { 97 | telegram_stat.errori++; 98 | console.log(err) 99 | 100 | istanza_bot.sendMessage( 101 | res_array[i].toSend.chat_id, 102 | parseError_parser(err, to_check) 103 | ).catch(function (err2) { 104 | telegram_stat.errori++; 105 | console.log(err2) 106 | }); 107 | }); 108 | } 109 | } 110 | 111 | // TO EDIT 112 | if (res_array[i].toEdit) { 113 | let to_return = { 114 | new_text: (typeof res_array[i].toEdit.message_text != "undefined" ? res_array[i].toEdit.message_text : res_array[i].toEdit.message_txt), 115 | options: { 116 | parse_mode: res_array[i].toEdit.options.parse_mode, 117 | disable_web_page_preview: true, 118 | reply_markup: res_array[i].toEdit.options.reply_markup 119 | } 120 | }; 121 | if (typeof res_array[i].toEdit.inline_message_id != "undefined") { 122 | to_return.options.inline_message_id = res_array[i].toEdit.inline_message_id; 123 | 124 | } else { 125 | to_return.options.chat_id = res_array[i].toEdit.chat_id; 126 | to_return.options.message_id = res_array[i].toEdit.mess_id; 127 | 128 | } 129 | telegram_stat.sent_msg++; 130 | 131 | istanza_bot.editMessageText( 132 | to_return.new_text, 133 | to_return.options 134 | ).catch(function (err_2) { 135 | console.error("Errore toEdit: "); 136 | console.log("Codice " + err_2.code); 137 | console.log(`chat_id ${to_return.options.chat_id}`); 138 | console.log(`message_id ${to_return.options.message_id}`); 139 | console.log(`inline_message_id ${to_return.options.inline_message_id}`); 140 | 141 | console.error(err_2.response.body); 142 | telegram_stat.errori++; 143 | 144 | 145 | // al0_bot.sendMessage( 146 | // res_array[i].toEdit.chat_id, 147 | // parseError_parser(err, res_array[i].toEdit.message_text) 148 | // ); 149 | }); 150 | } 151 | 152 | // SEND FILE 153 | if (typeof (res_array[i].sendFile) != "undefined") { 154 | console.log(res_array[i].sendFile); 155 | 156 | istanza_bot.sendDocument( 157 | res_array[i].sendFile.chat_id, 158 | res_array[i].sendFile.file, 159 | res_array[i].sendFile.message, 160 | res_array[i].sendFile.options 161 | ).catch(function (err) { 162 | telegram_stat.errori++; 163 | console.log("!toDelete -> "); 164 | console.log(err.response.body); 165 | }); 166 | } 167 | 168 | // DELETE (DELAYED) 169 | if (typeof (res_array[i].delayDelete) != "undefined") { 170 | delayDelete(res_array[i].delayDelete.chat_id, res_array[i].delayDelete.message_id, res_array[i].delayDelete.ms); 171 | } 172 | 173 | } 174 | } 175 | } 176 | module.exports.bigSend = bigSend; 177 | 178 | 179 | async function bigResponse(risposte, istanza_bot) { 180 | let res_array = []; 181 | 182 | // Normalizzo 183 | if (!(risposte instanceof Array)) { 184 | res_array.push(risposte); 185 | } else { 186 | res_array = risposte.slice(0, risposte.length); 187 | } 188 | 189 | let query_result = res_array.filter(function (sing) { 190 | return typeof sing.query != "undefined"; 191 | })[0]; 192 | 193 | let query_sent; 194 | // Prima devo inviare la query… 195 | try { 196 | query_sent = await istanza_bot.answerCallbackQuery( 197 | query_result.query.id, 198 | query_result.query.options 199 | ); 200 | } catch (err) { 201 | console.error("Errore Query: "); 202 | telegram_stat.errori++; 203 | 204 | console.log(err) 205 | query_sent = undefined; 206 | } 207 | //console.log(query_sent); 208 | 209 | // È come la bigSend da qui… Le unirò a breve… 210 | for (let i = 0; i < res_array.length; i++) { 211 | 212 | // Per messaggi da eliminare dopo un delay 213 | if (res_array[i].delayDelete) { 214 | delayDelete(res_array[i].delayDelete.chat_id, res_array[i].delayDelete.message_id, res_array[i].delayDelete.ms); 215 | } 216 | 217 | if (res_array[i].toDelete) { 218 | istanza_bot.deleteMessage( 219 | res_array[i].toDelete.chat_id, 220 | res_array[i].toDelete.mess_id 221 | ).catch(function (err_1) { 222 | console.error("Errore toDelete: "); 223 | telegram_stat.errori++; 224 | console.log(err_1.response.body); 225 | }); 226 | } 227 | 228 | if (res_array[i].toEdit) { 229 | let to_return = { 230 | new_text: (typeof res_array[i].toEdit.message_text != "undefined" ? res_array[i].toEdit.message_text : res_array[i].toEdit.message_txt), 231 | options: { 232 | parse_mode: res_array[i].toEdit.options.parse_mode, 233 | disable_web_page_preview: true, 234 | reply_markup: res_array[i].toEdit.options.reply_markup 235 | } 236 | }; 237 | if (typeof res_array[i].toEdit.inline_message_id != "undefined") { 238 | to_return.options.inline_message_id = res_array[i].toEdit.inline_message_id; 239 | 240 | } else { 241 | to_return.options.chat_id = res_array[i].toEdit.chat_id; 242 | to_return.options.message_id = res_array[i].toEdit.mess_id; 243 | 244 | } 245 | 246 | telegram_stat.sent_msg++; 247 | 248 | istanza_bot.editMessageText( 249 | to_return.new_text, 250 | to_return.options 251 | ).catch(function (err_2) { 252 | console.log("Errore toEdit: "); 253 | console.log("Codice " + err_2.code); 254 | console.log(`chat_id ${to_return.options.chat_id}`); 255 | console.log(`message_id ${to_return.options.message_id}`); 256 | console.log(`inline_message_id ${to_return.options.inline_message_id}`); 257 | console.error(err_2.response.body); 258 | console.log(to_return) 259 | telegram_stat.errori++; 260 | }); 261 | } 262 | 263 | if (res_array[i].editMarkup) { 264 | console.log(res_array[i].editMarkup.reply_markup); 265 | telegram_stat.sent_msg++; 266 | 267 | istanza_bot.editMessageReplyMarkup( 268 | res_array[i].editMarkup.reply_markup, 269 | { 270 | chat_id: res_array[i].editMarkup.chat_id, 271 | message_id: res_array[i].editMarkup.message_id, 272 | inline_message_id: res_array[i].editMarkup.query_id 273 | } 274 | ).catch(function (err_3) { 275 | console.log("Errore editMarkup: "); 276 | console.log("Codice " + err_3.code); 277 | console.error(err_3.response.body); 278 | telegram_stat.errori++; 279 | 280 | 281 | // al0_bot.sendMessage( 282 | // res_array[i].toEdit.chat_id, 283 | // parseError_parser(err, res_array[i].toEdit.message_text) 284 | // ); 285 | }); 286 | } 287 | 288 | if (res_array[i].toSend) { 289 | let actual_text = (typeof res_array[i].toSend.message_text != "undefined" ? res_array[i].toSend.message_text : res_array[i].toSend.message_txt); 290 | let charCount = actual_text.length; 291 | if (charCount >= 3500) { 292 | let arr = chunkSubstr(actual_text, 100); 293 | for (let l = 0; l < arr.length; l++) { 294 | telegram_stat.sent_msg++; 295 | 296 | istanza_bot.sendMessage( 297 | res_array[i].toSend.chat_id, 298 | arr[l], 299 | res_array[i].toSend.options 300 | ).catch(function (err_4) { 301 | console.error("> Errore query.bigSend(), l_index: " + l); 302 | telegram_stat.errori++; 303 | 304 | istanza_bot.sendMessage( 305 | res_array[i].toSend.chat_id, 306 | parseError_parser(err_4, arr[l]) 307 | ); 308 | }); 309 | } 310 | } else { 311 | telegram_stat.sent_msg++; 312 | 313 | istanza_bot.sendMessage( 314 | res_array[i].toSend.chat_id, 315 | actual_text, 316 | res_array[i].toSend.options 317 | ).catch(function (err_6) { 318 | console.error("> Errore query.toSend()"); 319 | telegram_stat.errori++; 320 | 321 | console.log(res_array[i].toSend); 322 | telegram_stat.sent_msg++; 323 | 324 | istanza_bot.sendMessage( 325 | res_array[i].toSend.chat_id, 326 | parseError_parser(err_6, actual_text) 327 | ); 328 | }); 329 | } 330 | } 331 | 332 | if (res_array[i].sendFile) { 333 | istanza_bot.sendDocument( 334 | res_array[i].sendFile.chat_id, 335 | res_array[i].sendFile.file, 336 | res_array[i].sendFile.message, 337 | res_array[i].sendFile.options 338 | ).catch(function (err_1) { 339 | console.error("Errore sendFile: "); 340 | console.log(err_1.response.body); 341 | }); 342 | } 343 | 344 | 345 | 346 | } 347 | } 348 | module.exports.bigResponse = bigResponse; 349 | 350 | 351 | 352 | function delayDelete(chat_id, mess_id, ms, istanza_bot) { 353 | return new Promise(async function (delay_del) { 354 | await sleep(ms); 355 | return istanza_bot.deleteMessage(chat_id, mess_id).catch(function (err) { 356 | telegram_stat.errori++; 357 | return true; 358 | }).catch(function (err) { 359 | telegram_stat.errori++; 360 | return false; 361 | }); 362 | }) 363 | } --------------------------------------------------------------------------------