├── .gitignore ├── LICENSE ├── README.md ├── bot ├── analytics.js ├── floodProtect.js ├── init.js ├── parseSauceNao.js └── request.js ├── package-lock.json ├── package.json ├── settings ├── private.js.example └── settings.js └── tools └── tools.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | # private 40 | settings/private.js 41 | 42 | # trash 43 | **/*.js~ 44 | 45 | # note 46 | note.txt -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [@reverseSearchBot](https://t.me/reverseSearchBot) 2 | 3 | Finds the source of an image/GIF/sticker/video or identify the anime character, show, episode & time from its screenshot. Also works in groups if you reply to an image with commands "/source" and "/sauce". Use the inline mode for URL lookups and sharing the sauce with buttons. 4 | 5 | Scrapes from ~~Tineye~~ (fuck) and Saucenow. Doesn't use the standard API. 6 | 7 | A more user friendly and functional rewrite of https://github.com/cod119/TeleSaucenao_bot 8 | 9 | ### Usage: 10 | 11 | Rename settings/private.js.example to .js after putting the required keys/tokens and constants. 12 | 13 | `npm i` 14 | 15 | `npm start` 16 | -------------------------------------------------------------------------------- /bot/analytics.js: -------------------------------------------------------------------------------- 1 | import { privateSettings, urls } from "../settings/settings.js"; 2 | 3 | export const track = async (msgFrom, eventType, eventProps) => { 4 | if (!msgFrom) return; 5 | const lang = msgFrom.language_code || "undefined"; 6 | 7 | const data = { 8 | api_key: privateSettings.analKey, 9 | events: [ 10 | { 11 | platform: privateSettings.botName, 12 | ip: "$remote", 13 | event_type: eventType, 14 | event_properties: eventProps, 15 | user_id: msgFrom.id, 16 | language: lang, 17 | }, 18 | ], 19 | }; 20 | 21 | try { 22 | await fetch(urls.analUrl, { 23 | headers: { 24 | Accept: "*/*", 25 | "Content-Type": "application/json", 26 | }, 27 | method: "POST", 28 | body: JSON.stringify(data), 29 | }); 30 | } catch (e) { 31 | console.log("anal failed " + e); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /bot/floodProtect.js: -------------------------------------------------------------------------------- 1 | import { floodProtect } from "../settings/settings.js"; 2 | import { LRUCache } from "lru-cache"; 3 | const userLRU = new LRUCache({ max: 100 }); 4 | 5 | export const isFlooding = (ctx) => { 6 | if (!ctx.message || !ctx.from || !ctx.message.date) return false; 7 | let userId = ctx.from.id; 8 | let user = userLRU.get(userId); 9 | let now = ctx.message.date; 10 | let diffSecs; 11 | if (!user) { 12 | diffSecs = 0; 13 | user = { lastTime: now, numMsgs: 1, warned: false }; 14 | userLRU.set(userId, user); 15 | } else { 16 | diffSecs = now - user.lastTime; 17 | 18 | user.lastTime = now; 19 | user.numMsgs++; 20 | } 21 | if (diffSecs < floodProtect.interval) { 22 | if (user.numMsgs > floodProtect.msgLimit) { 23 | if (!user.warned) { 24 | ctx.reply(floodProtect.message); 25 | user.warned = true; 26 | } 27 | console.log(userId + " is flooding"); 28 | return true; //flooding 29 | } else return false; 30 | } else { 31 | user.numMsgs = 2; 32 | // user.warned = false; 33 | return false; 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /bot/init.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | global.debug = false; 4 | 5 | import { 6 | privateSettings, 7 | msgs, 8 | id_buttonName, 9 | keywords, 10 | commands, 11 | } from "../settings/settings.js"; 12 | 13 | import { Telegraf, Markup } from "telegraf"; 14 | import { errInFetch, fetchSauceNao } from "./request.js"; 15 | import { parseSauceNao } from "./parseSauceNao.js"; 16 | import { isSupportedExt, editMessageText } from "../tools/tools.js"; 17 | import { track } from "./analytics.js"; 18 | import { isFlooding } from "./floodProtect.js"; 19 | import { message } from "telegraf/filters"; 20 | 21 | const bot = new Telegraf(privateSettings.botToken); 22 | 23 | process.on("unhandledRejection", (e) => { 24 | console.log("unhandledRejection:"); 25 | console.dir(e); 26 | }); 27 | 28 | const loadingKb = Markup.inlineKeyboard([ 29 | Markup.button.callback(id_buttonName.loading, "noop"), 30 | ]); 31 | 32 | bot.start((ctx) => ctx.reply(msgs.help, { parse_mode: "HTML" })); 33 | 34 | //added to group 35 | bot.on(message("new_chat_members"), (ctx) => { 36 | const token = privateSettings.botToken; 37 | const myId = token.substring(0, token.indexOf(":")); 38 | if (ctx.message.new_chat_participant.id == myId) 39 | ctx.reply(msgs.help, { parse_mode: "HTML" }); 40 | }); 41 | const keywordResponse = (ctx) => { 42 | if (isFlooding(ctx)) return; 43 | const rmsg = ctx.message.reply_to_message; 44 | 45 | if (rmsg && (rmsg.photo || rmsg.document || rmsg.sticker || rmsg.video)) { 46 | if ( 47 | (rmsg.forward_from && rmsg.forward_from.id == 792028928) || 48 | rmsg.from.id == 792028928 49 | ) { 50 | ctx.reply("LOL no cheating"); 51 | return; 52 | } 53 | getSauceFromFile(rmsg); 54 | track(ctx.from, "sauce_keyword", { text: ctx.message.text }); 55 | } else if (ctx.message.text.indexOf("/") == 0) { 56 | ctx.reply(msgs.keywordHelp, { 57 | parse_mode: "HTML", 58 | reply_to_message_id: ctx.message.message_id, 59 | }); 60 | track(ctx.from, "sauce_click_only"); 61 | } 62 | }; 63 | 64 | bot.hears(keywords, keywordResponse); 65 | bot.command(commands, keywordResponse); 66 | bot.command("help", (ctx) => ctx.reply(msgs.help, { parse_mode: "HTML" })); 67 | bot.command("privacy", (ctx) => 68 | ctx.reply(msgs.privacyPolicy, { parse_mode: "HTML" }) 69 | ); 70 | 71 | bot.on( 72 | [message("photo"), message("sticker"), message("document"), message("video")], 73 | (ctx) => { 74 | if (ctx.message.forward_from && ctx.message.forward_from.id == 792028928) { 75 | ctx.reply("LOL no cheating"); 76 | return; 77 | } 78 | 79 | if ( 80 | (ctx.message.chat.type == "private" && !isFlooding(ctx)) || 81 | (privateSettings.favouriteGroups.indexOf(ctx.message.chat.id) > -1 && 82 | ctx.message.photo) 83 | ) 84 | getSauceFromFile(ctx.message); 85 | } 86 | ); 87 | 88 | const getSauceFromFile = (msg) => { 89 | if (msg.photo && msg.photo.length > 0) { 90 | msg.fileId = msg.photo[msg.photo.length - 1].file_id; 91 | track(msg.from, "query", { type: "photo" }); 92 | } else if (msg.sticker) { 93 | if (msg.sticker.is_animated) msg.fileId = msg.sticker.thumb.file_id; 94 | else msg.fileId = msg.sticker.file_id; 95 | track(msg.from, "query", { type: "sticker" }); 96 | } else if (msg.document) { 97 | if (isSupportedExt(msg.document.file_name)) { 98 | msg.fileId = msg.document.file_id; 99 | track(msg.from, "query", { type: "file" }); 100 | } else if (msg.document.mime_type == "video/mp4") { 101 | if (msg.document.thumb) { 102 | msg.fileId = msg.document.thumb.file_id; 103 | track(msg.from, "query", { type: "video" }); 104 | } else return; 105 | } 106 | } else if (msg.video && msg.video.thumb) { 107 | msg.fileId = msg.video.thumb.file_id; 108 | track(msg.from, "query", { type: "video" }); 109 | } else return; 110 | bot.telegram 111 | .sendMessage(msg.chat.id, msgs.loading, { 112 | parse_mode: "HTML", 113 | reply_to_message_id: msg.message_id, 114 | ...loadingKb, 115 | }) 116 | .then((res) => { 117 | getSauce(msg, res); 118 | }); 119 | }; 120 | 121 | const getSauce = async (msg, editMsg) => { 122 | editMsg.fileId = msg.fileId; 123 | editMsg.url = msg.url; 124 | editMsg.origFrom = msg.from; 125 | if (msg.url) { 126 | await request(msg.url, bot, editMsg); 127 | } else { 128 | try { 129 | const file = await bot.telegram.getFile(msg.fileId); 130 | if (global.debug) console.log("file is", file); 131 | 132 | const url = 133 | "https://api.telegram.org/file/bot" + 134 | privateSettings.botToken + 135 | "/" + 136 | file.file_path; 137 | await request(url, bot, editMsg); 138 | } catch (e) { 139 | console.log("invalidFileId"); 140 | await editMessageText(bot, editMsg, msgs.invalidFileId, { 141 | parse_mode: "HTML", 142 | }); 143 | } 144 | } 145 | }; 146 | 147 | const request = async (url, bot, editMsg) => { 148 | editMsg.url = url; 149 | 150 | try { 151 | const res = await fetchSauceNao(url, editMsg); 152 | const parsedRes = parseSauceNao(res, editMsg); 153 | if (parsedRes) { 154 | const { displayText, markup, preview } = parsedRes; 155 | try { 156 | await editMessageText(bot, editMsg, displayText, { 157 | parse_mode: "HTML", 158 | ...markup, 159 | disable_web_page_preview: !preview, 160 | }); 161 | } catch (e) { 162 | errInFetch(e); 163 | } 164 | } 165 | } catch (err) { 166 | let errDisplayText; 167 | if (err.message == msgs.zeroResult) errDisplayText = msgs.zeroResult; 168 | else if (err.message == msgs.reachLimitation) { 169 | errInFetch(err); 170 | errDisplayText = msgs.reachLimitation; 171 | } else { 172 | errInFetch(err); 173 | errDisplayText = msgs.unknownError; 174 | } 175 | await editMessageText(bot, editMsg, errDisplayText, { 176 | parse_mode: "HTML", 177 | disable_web_page_preview: true, 178 | }); 179 | } 180 | }; 181 | 182 | bot.launch({ 183 | dropPendingUpdates: true, 184 | }); 185 | // await bot.telegram.deleteWebhook() 186 | // bot.launch({ 187 | // webhook: { 188 | // domain: privateSettings.webhookEndpoint, 189 | // hookPath: privateSettings.webhookEndpoint.substring( 190 | // privateSettings.webhookEndpoint.lastIndexOf("/") 191 | // ), 192 | // port: privateSettings.webhookPort, 193 | // host: privateSettings.webhookHost, 194 | // }, 195 | // }); 196 | -------------------------------------------------------------------------------- /bot/parseSauceNao.js: -------------------------------------------------------------------------------- 1 | import { Markup } from "telegraf"; 2 | import { urls, msgs, sauceNaoParams } from "../settings/settings.js"; 3 | import { json2query } from "../tools/tools.js"; 4 | import { track } from "./analytics.js"; 5 | import { load } from "cheerio"; 6 | 7 | export const parseSauceNao = (response, editMsg) => { 8 | const $ = load(response); 9 | let found = false; 10 | const preview = false; 11 | const bList = []; 12 | 13 | const results = []; 14 | const links = {}; 15 | const content = { _title: "", _text: "" }; 16 | let displayText = ""; 17 | 18 | $(".resulttablecontent").each((i, elem) => { 19 | const percent = parseFloat($(elem).find(".resultsimilarityinfo").text()); 20 | if ( 21 | results.length && 22 | Math.abs(results[0].percent - percent) > sauceNaoParams.tolerance 23 | ) 24 | return; 25 | if (percent < sauceNaoParams.minSimilarity) return; 26 | 27 | found = true; 28 | 29 | $(elem) 30 | .find(".resultcontentcolumn a") 31 | .each((i, elem) => { 32 | let text; 33 | if ($(elem).prev()) text = $(elem).prev().text(); 34 | if (!text) return; 35 | text = text.replace("Member", "Artist"); 36 | 37 | links[text] = $(elem).attr("href"); 38 | if (text == "Artist" && !content._byUsername) 39 | content._byUsername = $(elem).text(); 40 | $(elem).prev().remove(); 41 | $(elem).remove(); 42 | }); 43 | 44 | $(elem) 45 | .find(".resulttitle, .resultcontentcolumn") 46 | .each((i, elem) => { 47 | let isCharactersDiv = false; 48 | 49 | let lines = $(elem) 50 | .html() 51 | .replace(/
|
/g, "\n") 52 | .trim(); 53 | lines = $(lines).text(); 54 | 55 | if (lines) { 56 | lines = lines.split("\n"); 57 | for (i in lines) { 58 | if (!lines[i].trim()) continue; 59 | const splits = lines[i].split(": "); 60 | let key = splits[0].trim(); 61 | if (splits.length == 2 && splits[1]) { 62 | key = key.replace("Est Time", "Time").replace("Creator", "By"); 63 | if (!content[key]) content[key] = splits[1].trim(); 64 | } else if (key) { 65 | //a character block has multiple characters 66 | //seperated by \n and nothing else (i think) 67 | if (key == "Characters") { 68 | isCharactersDiv = true; 69 | content.Characters = ""; 70 | } else if (isCharactersDiv) { 71 | content.Characters += key; 72 | if (i != lines.length - 1) content.Characters += ", "; 73 | } else if ($(elem).is(".resulttitle") && !content._title) 74 | content._title += lines[i].trim() + "\n"; 75 | else content._text += " " + lines[i].trim() + "\n"; 76 | } 77 | } 78 | } 79 | }); 80 | 81 | $(elem) 82 | .find(".resultmiscinfo > a") 83 | .each((i, elem) => { 84 | let text = $(elem).children().attr("src"); 85 | text = text.substring(text.lastIndexOf("/") + 1, text.lastIndexOf(".")); 86 | if (links[text]) { 87 | return; 88 | } 89 | links[text] = $(elem).attr("href"); 90 | if (text == "anidb") { 91 | // extract episode number 92 | let title; 93 | const matches = content._title.match(/(.+) - ([\d]+)\n$/); 94 | if (matches && matches.length == 3) { 95 | title = matches[1]; 96 | content._title = title + " (Ep. " + matches[2] + ")\n"; 97 | } else title = content._title; 98 | 99 | links["MAL"] = urls.mal + json2query({ q: title }); 100 | } 101 | }); 102 | 103 | results.push({ percent: percent }); 104 | }); 105 | 106 | if (!found) { 107 | throw new Error(msgs.zeroResult); 108 | } 109 | if (content._title) displayText = "" + content._title + "" + "\n"; 110 | 111 | delete content._title; 112 | 113 | if (!content.By && content._byUsername) content._byUsername = content.By; 114 | 115 | if (content.Characters) { 116 | displayText += "Character: " + content.Characters + "\n"; 117 | delete content.Characters; 118 | } 119 | if (content.Material) { 120 | displayText += "Material: " + content.Material + "\n"; 121 | delete content.Material; 122 | } 123 | 124 | for (let key in content) { 125 | if (!(key == "_text" || key == "JPTitle")) 126 | displayText += "" + key + ": " + content[key] + "\n"; 127 | } 128 | // put blank line if > 1 line 129 | if ((content._text.match(/\n/g) || []).length > 1) 130 | content._text = "\n" + content._text; 131 | 132 | displayText += content._text; 133 | if (!displayText.trim()) displayText = "-no title-"; 134 | 135 | let ctr = 0; 136 | for (const key in links) { 137 | if (ctr >= 6) 138 | //max 6 buttons or 3 lines 139 | break; 140 | let text = key.replace(/:/g, "").replace(/ ID/g, ""); 141 | let url = links[key]; 142 | if (url.startsWith("//")) url = "http:" + url; 143 | if (ctr == 0) text = "View on " + text; 144 | bList.push(Markup.button.url(text, url)); 145 | ctr++; 146 | } 147 | 148 | const markup = Markup.inlineKeyboard(bList, { 149 | wrap: (btn, index, currentRow) => currentRow.length >= 3, 150 | }); 151 | 152 | track(editMsg.origFrom, "sauce_found_saucenao"); 153 | 154 | return { displayText, markup, preview }; 155 | }; 156 | -------------------------------------------------------------------------------- /bot/request.js: -------------------------------------------------------------------------------- 1 | import { socksDispatcher } from "fetch-socks"; 2 | import { exec } from "child_process"; 3 | import { LRUCache } from "lru-cache"; 4 | 5 | import { 6 | privateSettings, 7 | urls, 8 | sauceNaoParams, 9 | msgs, 10 | userAgent, 11 | } from "../settings/settings.js"; 12 | import { json2query } from "../tools/tools.js"; 13 | import { track } from "./analytics.js"; 14 | 15 | const dispatcher = socksDispatcher({ 16 | type: 5, 17 | host: privateSettings.socksProxyHost, 18 | port: privateSettings.socksProxyPort, 19 | }); 20 | 21 | const cache = new LRUCache({ max: 200, ttl: 1000 * 60 * 60 * 24 }); 22 | const myFetch = async (url, editMsg, options) => { 23 | const hit = cache.get(url); 24 | 25 | if (hit && editMsg && editMsg.origFrom) { 26 | track(editMsg.origFrom, "cache_hit"); 27 | return hit; 28 | } 29 | options.dispatcher = dispatcher; 30 | options.headers = { 31 | "User-Agent": userAgent, 32 | }; 33 | 34 | const res = await fetch(url, options); 35 | if (res.status >= 200 && res.status < 300) { 36 | const txt = res.text(); 37 | cache.set(url, txt); 38 | 39 | return txt; 40 | } else { 41 | const error = new Error(res.statusText || res.status); 42 | error.response = res; 43 | if (options.params && options.params.url) error.url = options.params.url; 44 | throw error; 45 | } 46 | }; 47 | 48 | export function fetchSauceNao(url, editMsg) { 49 | const params = sauceNaoParams; 50 | params.url = url; 51 | const uurl = urls.sauceNao + json2query(params); 52 | 53 | return myFetch(uurl, editMsg, { params: params }); 54 | } 55 | 56 | export function errInFetch(err) { 57 | console.log("errInFetch"); 58 | 59 | if (err.response) { 60 | // The request was made, but the server responded with a status code 61 | // that falls out of the range of 2xx 62 | console.log("-----error.status is", err.response.status); 63 | if ( 64 | err.response.status && 65 | (err.response.status == 429 || err.response.status == 403) 66 | ) { 67 | let now = Date.now(); 68 | if ( 69 | now - proxy.lastReqTime > 100 * 1000 && 70 | privateSettings.changeProxyCommand 71 | ) { 72 | proxy.lastReqTime = now; 73 | exec(privateSettings.changeProxyCommand, (err, stdout, stderr) => { 74 | if (stdout) console.log(stdout); 75 | else if (stderr) console.log(stderr); 76 | }); 77 | } 78 | return new Error(msgs.reachLimitation); 79 | } 80 | } 81 | return err; 82 | } 83 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "saucenaobot", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "saucenaobot", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "async-mutex": "^0.5.0", 13 | "cheerio": "^1.0.0-rc.12", 14 | "fetch-socks": "^1.3.0", 15 | "lru-cache": "^10.3.1", 16 | "telegraf": "^4.16.3" 17 | } 18 | }, 19 | "node_modules/@telegraf/types": { 20 | "version": "7.1.0", 21 | "resolved": "https://registry.npmjs.org/@telegraf/types/-/types-7.1.0.tgz", 22 | "integrity": "sha512-kGevOIbpMcIlCDeorKGpwZmdH7kHbqlk/Yj6dEpJMKEQw5lk0KVQY0OLXaCswy8GqlIVLd5625OB+rAntP9xVw==" 23 | }, 24 | "node_modules/abort-controller": { 25 | "version": "3.0.0", 26 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 27 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 28 | "dependencies": { 29 | "event-target-shim": "^5.0.0" 30 | }, 31 | "engines": { 32 | "node": ">=6.5" 33 | } 34 | }, 35 | "node_modules/async-mutex": { 36 | "version": "0.5.0", 37 | "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", 38 | "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", 39 | "dependencies": { 40 | "tslib": "^2.4.0" 41 | } 42 | }, 43 | "node_modules/boolbase": { 44 | "version": "1.0.0", 45 | "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", 46 | "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" 47 | }, 48 | "node_modules/buffer-alloc": { 49 | "version": "1.2.0", 50 | "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", 51 | "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", 52 | "dependencies": { 53 | "buffer-alloc-unsafe": "^1.1.0", 54 | "buffer-fill": "^1.0.0" 55 | } 56 | }, 57 | "node_modules/buffer-alloc-unsafe": { 58 | "version": "1.1.0", 59 | "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", 60 | "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" 61 | }, 62 | "node_modules/buffer-fill": { 63 | "version": "1.0.0", 64 | "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", 65 | "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" 66 | }, 67 | "node_modules/cheerio": { 68 | "version": "1.0.0-rc.12", 69 | "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", 70 | "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", 71 | "dependencies": { 72 | "cheerio-select": "^2.1.0", 73 | "dom-serializer": "^2.0.0", 74 | "domhandler": "^5.0.3", 75 | "domutils": "^3.0.1", 76 | "htmlparser2": "^8.0.1", 77 | "parse5": "^7.0.0", 78 | "parse5-htmlparser2-tree-adapter": "^7.0.0" 79 | }, 80 | "engines": { 81 | "node": ">= 6" 82 | }, 83 | "funding": { 84 | "url": "https://github.com/cheeriojs/cheerio?sponsor=1" 85 | } 86 | }, 87 | "node_modules/cheerio-select": { 88 | "version": "2.1.0", 89 | "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", 90 | "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", 91 | "dependencies": { 92 | "boolbase": "^1.0.0", 93 | "css-select": "^5.1.0", 94 | "css-what": "^6.1.0", 95 | "domelementtype": "^2.3.0", 96 | "domhandler": "^5.0.3", 97 | "domutils": "^3.0.1" 98 | }, 99 | "funding": { 100 | "url": "https://github.com/sponsors/fb55" 101 | } 102 | }, 103 | "node_modules/css-select": { 104 | "version": "5.1.0", 105 | "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", 106 | "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", 107 | "dependencies": { 108 | "boolbase": "^1.0.0", 109 | "css-what": "^6.1.0", 110 | "domhandler": "^5.0.2", 111 | "domutils": "^3.0.1", 112 | "nth-check": "^2.0.1" 113 | }, 114 | "funding": { 115 | "url": "https://github.com/sponsors/fb55" 116 | } 117 | }, 118 | "node_modules/css-what": { 119 | "version": "6.1.0", 120 | "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", 121 | "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", 122 | "engines": { 123 | "node": ">= 6" 124 | }, 125 | "funding": { 126 | "url": "https://github.com/sponsors/fb55" 127 | } 128 | }, 129 | "node_modules/dom-serializer": { 130 | "version": "2.0.0", 131 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", 132 | "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", 133 | "dependencies": { 134 | "domelementtype": "^2.3.0", 135 | "domhandler": "^5.0.2", 136 | "entities": "^4.2.0" 137 | }, 138 | "funding": { 139 | "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" 140 | } 141 | }, 142 | "node_modules/domelementtype": { 143 | "version": "2.3.0", 144 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", 145 | "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", 146 | "funding": [ 147 | { 148 | "type": "github", 149 | "url": "https://github.com/sponsors/fb55" 150 | } 151 | ] 152 | }, 153 | "node_modules/domhandler": { 154 | "version": "5.0.3", 155 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", 156 | "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", 157 | "dependencies": { 158 | "domelementtype": "^2.3.0" 159 | }, 160 | "engines": { 161 | "node": ">= 4" 162 | }, 163 | "funding": { 164 | "url": "https://github.com/fb55/domhandler?sponsor=1" 165 | } 166 | }, 167 | "node_modules/domutils": { 168 | "version": "3.1.0", 169 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", 170 | "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", 171 | "dependencies": { 172 | "dom-serializer": "^2.0.0", 173 | "domelementtype": "^2.3.0", 174 | "domhandler": "^5.0.3" 175 | }, 176 | "funding": { 177 | "url": "https://github.com/fb55/domutils?sponsor=1" 178 | } 179 | }, 180 | "node_modules/entities": { 181 | "version": "4.5.0", 182 | "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", 183 | "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", 184 | "engines": { 185 | "node": ">=0.12" 186 | }, 187 | "funding": { 188 | "url": "https://github.com/fb55/entities?sponsor=1" 189 | } 190 | }, 191 | "node_modules/event-target-shim": { 192 | "version": "5.0.1", 193 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 194 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", 195 | "engines": { 196 | "node": ">=6" 197 | } 198 | }, 199 | "node_modules/fetch-socks": { 200 | "version": "1.3.0", 201 | "resolved": "https://registry.npmjs.org/fetch-socks/-/fetch-socks-1.3.0.tgz", 202 | "integrity": "sha512-Cq7O53hoNiVeOs6u54f8M/H/w2yzhmnTQ3tcAJj9FNKYOeNGmt8qNU1zpWOzJD09f0uqfmBXxLbzWPsnT6GcRw==", 203 | "dependencies": { 204 | "socks": "^2.8.1", 205 | "undici": "^6.10.1" 206 | } 207 | }, 208 | "node_modules/htmlparser2": { 209 | "version": "8.0.2", 210 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", 211 | "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", 212 | "funding": [ 213 | "https://github.com/fb55/htmlparser2?sponsor=1", 214 | { 215 | "type": "github", 216 | "url": "https://github.com/sponsors/fb55" 217 | } 218 | ], 219 | "dependencies": { 220 | "domelementtype": "^2.3.0", 221 | "domhandler": "^5.0.3", 222 | "domutils": "^3.0.1", 223 | "entities": "^4.4.0" 224 | } 225 | }, 226 | "node_modules/ip-address": { 227 | "version": "9.0.5", 228 | "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", 229 | "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", 230 | "dependencies": { 231 | "jsbn": "1.1.0", 232 | "sprintf-js": "^1.1.3" 233 | }, 234 | "engines": { 235 | "node": ">= 12" 236 | } 237 | }, 238 | "node_modules/jsbn": { 239 | "version": "1.1.0", 240 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", 241 | "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" 242 | }, 243 | "node_modules/lru-cache": { 244 | "version": "10.3.1", 245 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.3.1.tgz", 246 | "integrity": "sha512-9/8QXrtbGeMB6LxwQd4x1tIMnsmUxMvIH/qWGsccz6bt9Uln3S+sgAaqfQNhbGA8ufzs2fHuP/yqapGgP9Hh2g==", 247 | "engines": { 248 | "node": ">=18" 249 | } 250 | }, 251 | "node_modules/mri": { 252 | "version": "1.2.0", 253 | "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", 254 | "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", 255 | "engines": { 256 | "node": ">=4" 257 | } 258 | }, 259 | "node_modules/nth-check": { 260 | "version": "2.1.1", 261 | "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", 262 | "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", 263 | "dependencies": { 264 | "boolbase": "^1.0.0" 265 | }, 266 | "funding": { 267 | "url": "https://github.com/fb55/nth-check?sponsor=1" 268 | } 269 | }, 270 | "node_modules/p-timeout": { 271 | "version": "4.1.0", 272 | "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-4.1.0.tgz", 273 | "integrity": "sha512-+/wmHtzJuWii1sXn3HCuH/FTwGhrp4tmJTxSKJbfS+vkipci6osxXM5mY0jUiRzWKMTgUT8l7HFbeSwZAynqHw==", 274 | "engines": { 275 | "node": ">=10" 276 | } 277 | }, 278 | "node_modules/parse5": { 279 | "version": "7.1.2", 280 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", 281 | "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", 282 | "dependencies": { 283 | "entities": "^4.4.0" 284 | }, 285 | "funding": { 286 | "url": "https://github.com/inikulin/parse5?sponsor=1" 287 | } 288 | }, 289 | "node_modules/parse5-htmlparser2-tree-adapter": { 290 | "version": "7.0.0", 291 | "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", 292 | "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", 293 | "dependencies": { 294 | "domhandler": "^5.0.2", 295 | "parse5": "^7.0.0" 296 | }, 297 | "funding": { 298 | "url": "https://github.com/inikulin/parse5?sponsor=1" 299 | } 300 | }, 301 | "node_modules/safe-compare": { 302 | "version": "1.1.4", 303 | "resolved": "https://registry.npmjs.org/safe-compare/-/safe-compare-1.1.4.tgz", 304 | "integrity": "sha512-b9wZ986HHCo/HbKrRpBJb2kqXMK9CEWIE1egeEvZsYn69ay3kdfl9nG3RyOcR+jInTDf7a86WQ1d4VJX7goSSQ==", 305 | "dependencies": { 306 | "buffer-alloc": "^1.2.0" 307 | } 308 | }, 309 | "node_modules/sandwich-stream": { 310 | "version": "2.0.2", 311 | "resolved": "https://registry.npmjs.org/sandwich-stream/-/sandwich-stream-2.0.2.tgz", 312 | "integrity": "sha512-jLYV0DORrzY3xaz/S9ydJL6Iz7essZeAfnAavsJ+zsJGZ1MOnsS52yRjU3uF3pJa/lla7+wisp//fxOwOH8SKQ==", 313 | "engines": { 314 | "node": ">= 0.10" 315 | } 316 | }, 317 | "node_modules/smart-buffer": { 318 | "version": "4.2.0", 319 | "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", 320 | "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", 321 | "engines": { 322 | "node": ">= 6.0.0", 323 | "npm": ">= 3.0.0" 324 | } 325 | }, 326 | "node_modules/socks": { 327 | "version": "2.8.1", 328 | "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.1.tgz", 329 | "integrity": "sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ==", 330 | "dependencies": { 331 | "ip-address": "^9.0.5", 332 | "smart-buffer": "^4.2.0" 333 | }, 334 | "engines": { 335 | "node": ">= 10.0.0", 336 | "npm": ">= 3.0.0" 337 | } 338 | }, 339 | "node_modules/sprintf-js": { 340 | "version": "1.1.3", 341 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", 342 | "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" 343 | }, 344 | "node_modules/telegraf": { 345 | "version": "4.16.3", 346 | "resolved": "https://registry.npmjs.org/telegraf/-/telegraf-4.16.3.tgz", 347 | "integrity": "sha512-yjEu2NwkHlXu0OARWoNhJlIjX09dRktiMQFsM678BAH/PEPVwctzL67+tvXqLCRQQvm3SDtki2saGO9hLlz68w==", 348 | "dependencies": { 349 | "@telegraf/types": "^7.1.0", 350 | "abort-controller": "^3.0.0", 351 | "debug": "^4.3.4", 352 | "mri": "^1.2.0", 353 | "node-fetch": "^2.7.0", 354 | "p-timeout": "^4.1.0", 355 | "safe-compare": "^1.1.4", 356 | "sandwich-stream": "^2.0.2" 357 | }, 358 | "bin": { 359 | "telegraf": "lib/cli.mjs" 360 | }, 361 | "engines": { 362 | "node": "^12.20.0 || >=14.13.1" 363 | } 364 | }, 365 | "node_modules/telegraf/node_modules/debug": { 366 | "version": "4.3.4", 367 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 368 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 369 | "dependencies": { 370 | "ms": "2.1.2" 371 | }, 372 | "engines": { 373 | "node": ">=6.0" 374 | }, 375 | "peerDependenciesMeta": { 376 | "supports-color": { 377 | "optional": true 378 | } 379 | } 380 | }, 381 | "node_modules/telegraf/node_modules/ms": { 382 | "version": "2.1.2", 383 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 384 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 385 | }, 386 | "node_modules/telegraf/node_modules/node-fetch": { 387 | "version": "2.7.0", 388 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", 389 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", 390 | "dependencies": { 391 | "whatwg-url": "^5.0.0" 392 | }, 393 | "engines": { 394 | "node": "4.x || >=6.0.0" 395 | }, 396 | "peerDependencies": { 397 | "encoding": "^0.1.0" 398 | }, 399 | "peerDependenciesMeta": { 400 | "encoding": { 401 | "optional": true 402 | } 403 | } 404 | }, 405 | "node_modules/tr46": { 406 | "version": "0.0.3", 407 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 408 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" 409 | }, 410 | "node_modules/tslib": { 411 | "version": "2.6.2", 412 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", 413 | "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" 414 | }, 415 | "node_modules/undici": { 416 | "version": "6.13.0", 417 | "resolved": "https://registry.npmjs.org/undici/-/undici-6.13.0.tgz", 418 | "integrity": "sha512-Q2rtqmZWrbP8nePMq7mOJIN98M0fYvSgV89vwl/BQRT4mDOeY2GXZngfGpcBBhtky3woM7G24wZV3Q304Bv6cw==", 419 | "engines": { 420 | "node": ">=18.0" 421 | } 422 | }, 423 | "node_modules/webidl-conversions": { 424 | "version": "3.0.1", 425 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 426 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 427 | }, 428 | "node_modules/whatwg-url": { 429 | "version": "5.0.0", 430 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 431 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 432 | "dependencies": { 433 | "tr46": "~0.0.3", 434 | "webidl-conversions": "^3.0.0" 435 | } 436 | } 437 | }, 438 | "dependencies": { 439 | "@telegraf/types": { 440 | "version": "7.1.0", 441 | "resolved": "https://registry.npmjs.org/@telegraf/types/-/types-7.1.0.tgz", 442 | "integrity": "sha512-kGevOIbpMcIlCDeorKGpwZmdH7kHbqlk/Yj6dEpJMKEQw5lk0KVQY0OLXaCswy8GqlIVLd5625OB+rAntP9xVw==" 443 | }, 444 | "abort-controller": { 445 | "version": "3.0.0", 446 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 447 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 448 | "requires": { 449 | "event-target-shim": "^5.0.0" 450 | } 451 | }, 452 | "async-mutex": { 453 | "version": "0.5.0", 454 | "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", 455 | "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", 456 | "requires": { 457 | "tslib": "^2.4.0" 458 | } 459 | }, 460 | "boolbase": { 461 | "version": "1.0.0", 462 | "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", 463 | "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" 464 | }, 465 | "buffer-alloc": { 466 | "version": "1.2.0", 467 | "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", 468 | "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", 469 | "requires": { 470 | "buffer-alloc-unsafe": "^1.1.0", 471 | "buffer-fill": "^1.0.0" 472 | } 473 | }, 474 | "buffer-alloc-unsafe": { 475 | "version": "1.1.0", 476 | "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", 477 | "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" 478 | }, 479 | "buffer-fill": { 480 | "version": "1.0.0", 481 | "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", 482 | "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" 483 | }, 484 | "cheerio": { 485 | "version": "1.0.0-rc.12", 486 | "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", 487 | "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", 488 | "requires": { 489 | "cheerio-select": "^2.1.0", 490 | "dom-serializer": "^2.0.0", 491 | "domhandler": "^5.0.3", 492 | "domutils": "^3.0.1", 493 | "htmlparser2": "^8.0.1", 494 | "parse5": "^7.0.0", 495 | "parse5-htmlparser2-tree-adapter": "^7.0.0" 496 | } 497 | }, 498 | "cheerio-select": { 499 | "version": "2.1.0", 500 | "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", 501 | "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", 502 | "requires": { 503 | "boolbase": "^1.0.0", 504 | "css-select": "^5.1.0", 505 | "css-what": "^6.1.0", 506 | "domelementtype": "^2.3.0", 507 | "domhandler": "^5.0.3", 508 | "domutils": "^3.0.1" 509 | } 510 | }, 511 | "css-select": { 512 | "version": "5.1.0", 513 | "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", 514 | "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", 515 | "requires": { 516 | "boolbase": "^1.0.0", 517 | "css-what": "^6.1.0", 518 | "domhandler": "^5.0.2", 519 | "domutils": "^3.0.1", 520 | "nth-check": "^2.0.1" 521 | } 522 | }, 523 | "css-what": { 524 | "version": "6.1.0", 525 | "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", 526 | "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" 527 | }, 528 | "dom-serializer": { 529 | "version": "2.0.0", 530 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", 531 | "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", 532 | "requires": { 533 | "domelementtype": "^2.3.0", 534 | "domhandler": "^5.0.2", 535 | "entities": "^4.2.0" 536 | } 537 | }, 538 | "domelementtype": { 539 | "version": "2.3.0", 540 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", 541 | "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" 542 | }, 543 | "domhandler": { 544 | "version": "5.0.3", 545 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", 546 | "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", 547 | "requires": { 548 | "domelementtype": "^2.3.0" 549 | } 550 | }, 551 | "domutils": { 552 | "version": "3.1.0", 553 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", 554 | "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", 555 | "requires": { 556 | "dom-serializer": "^2.0.0", 557 | "domelementtype": "^2.3.0", 558 | "domhandler": "^5.0.3" 559 | } 560 | }, 561 | "entities": { 562 | "version": "4.5.0", 563 | "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", 564 | "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" 565 | }, 566 | "event-target-shim": { 567 | "version": "5.0.1", 568 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 569 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" 570 | }, 571 | "fetch-socks": { 572 | "version": "1.3.0", 573 | "resolved": "https://registry.npmjs.org/fetch-socks/-/fetch-socks-1.3.0.tgz", 574 | "integrity": "sha512-Cq7O53hoNiVeOs6u54f8M/H/w2yzhmnTQ3tcAJj9FNKYOeNGmt8qNU1zpWOzJD09f0uqfmBXxLbzWPsnT6GcRw==", 575 | "requires": { 576 | "socks": "^2.8.1", 577 | "undici": "^6.10.1" 578 | } 579 | }, 580 | "htmlparser2": { 581 | "version": "8.0.2", 582 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", 583 | "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", 584 | "requires": { 585 | "domelementtype": "^2.3.0", 586 | "domhandler": "^5.0.3", 587 | "domutils": "^3.0.1", 588 | "entities": "^4.4.0" 589 | } 590 | }, 591 | "ip-address": { 592 | "version": "9.0.5", 593 | "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", 594 | "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", 595 | "requires": { 596 | "jsbn": "1.1.0", 597 | "sprintf-js": "^1.1.3" 598 | } 599 | }, 600 | "jsbn": { 601 | "version": "1.1.0", 602 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", 603 | "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==" 604 | }, 605 | "lru-cache": { 606 | "version": "10.3.1", 607 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.3.1.tgz", 608 | "integrity": "sha512-9/8QXrtbGeMB6LxwQd4x1tIMnsmUxMvIH/qWGsccz6bt9Uln3S+sgAaqfQNhbGA8ufzs2fHuP/yqapGgP9Hh2g==" 609 | }, 610 | "mri": { 611 | "version": "1.2.0", 612 | "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", 613 | "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==" 614 | }, 615 | "nth-check": { 616 | "version": "2.1.1", 617 | "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", 618 | "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", 619 | "requires": { 620 | "boolbase": "^1.0.0" 621 | } 622 | }, 623 | "p-timeout": { 624 | "version": "4.1.0", 625 | "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-4.1.0.tgz", 626 | "integrity": "sha512-+/wmHtzJuWii1sXn3HCuH/FTwGhrp4tmJTxSKJbfS+vkipci6osxXM5mY0jUiRzWKMTgUT8l7HFbeSwZAynqHw==" 627 | }, 628 | "parse5": { 629 | "version": "7.1.2", 630 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", 631 | "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", 632 | "requires": { 633 | "entities": "^4.4.0" 634 | } 635 | }, 636 | "parse5-htmlparser2-tree-adapter": { 637 | "version": "7.0.0", 638 | "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", 639 | "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", 640 | "requires": { 641 | "domhandler": "^5.0.2", 642 | "parse5": "^7.0.0" 643 | } 644 | }, 645 | "safe-compare": { 646 | "version": "1.1.4", 647 | "resolved": "https://registry.npmjs.org/safe-compare/-/safe-compare-1.1.4.tgz", 648 | "integrity": "sha512-b9wZ986HHCo/HbKrRpBJb2kqXMK9CEWIE1egeEvZsYn69ay3kdfl9nG3RyOcR+jInTDf7a86WQ1d4VJX7goSSQ==", 649 | "requires": { 650 | "buffer-alloc": "^1.2.0" 651 | } 652 | }, 653 | "sandwich-stream": { 654 | "version": "2.0.2", 655 | "resolved": "https://registry.npmjs.org/sandwich-stream/-/sandwich-stream-2.0.2.tgz", 656 | "integrity": "sha512-jLYV0DORrzY3xaz/S9ydJL6Iz7essZeAfnAavsJ+zsJGZ1MOnsS52yRjU3uF3pJa/lla7+wisp//fxOwOH8SKQ==" 657 | }, 658 | "smart-buffer": { 659 | "version": "4.2.0", 660 | "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", 661 | "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" 662 | }, 663 | "socks": { 664 | "version": "2.8.1", 665 | "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.1.tgz", 666 | "integrity": "sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ==", 667 | "requires": { 668 | "ip-address": "^9.0.5", 669 | "smart-buffer": "^4.2.0" 670 | } 671 | }, 672 | "sprintf-js": { 673 | "version": "1.1.3", 674 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", 675 | "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" 676 | }, 677 | "telegraf": { 678 | "version": "4.16.3", 679 | "resolved": "https://registry.npmjs.org/telegraf/-/telegraf-4.16.3.tgz", 680 | "integrity": "sha512-yjEu2NwkHlXu0OARWoNhJlIjX09dRktiMQFsM678BAH/PEPVwctzL67+tvXqLCRQQvm3SDtki2saGO9hLlz68w==", 681 | "requires": { 682 | "@telegraf/types": "^7.1.0", 683 | "abort-controller": "^3.0.0", 684 | "debug": "^4.3.4", 685 | "mri": "^1.2.0", 686 | "node-fetch": "^2.7.0", 687 | "p-timeout": "^4.1.0", 688 | "safe-compare": "^1.1.4", 689 | "sandwich-stream": "^2.0.2" 690 | }, 691 | "dependencies": { 692 | "debug": { 693 | "version": "4.3.4", 694 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 695 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 696 | "requires": { 697 | "ms": "2.1.2" 698 | } 699 | }, 700 | "ms": { 701 | "version": "2.1.2", 702 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 703 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 704 | }, 705 | "node-fetch": { 706 | "version": "2.7.0", 707 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", 708 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", 709 | "requires": { 710 | "whatwg-url": "^5.0.0" 711 | } 712 | } 713 | } 714 | }, 715 | "tr46": { 716 | "version": "0.0.3", 717 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 718 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" 719 | }, 720 | "tslib": { 721 | "version": "2.6.2", 722 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", 723 | "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" 724 | }, 725 | "undici": { 726 | "version": "6.13.0", 727 | "resolved": "https://registry.npmjs.org/undici/-/undici-6.13.0.tgz", 728 | "integrity": "sha512-Q2rtqmZWrbP8nePMq7mOJIN98M0fYvSgV89vwl/BQRT4mDOeY2GXZngfGpcBBhtky3woM7G24wZV3Q304Bv6cw==" 729 | }, 730 | "webidl-conversions": { 731 | "version": "3.0.1", 732 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 733 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" 734 | }, 735 | "whatwg-url": { 736 | "version": "5.0.0", 737 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 738 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 739 | "requires": { 740 | "tr46": "~0.0.3", 741 | "webidl-conversions": "^3.0.0" 742 | } 743 | } 744 | } 745 | } 746 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "saucenaobot", 3 | "version": "1.0.0", 4 | "description": "Reverse Image Search bot for Telegram. Check settings/private.js for the api keys and tokens required.", 5 | "main": "bot/init.js", 6 | "type": "module", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1", 9 | "start": "node bot/init.js" 10 | }, 11 | "author": "kawaiiDango", 12 | "bugs": { 13 | "url": "https://github.com/kawaiiDango/reverseSearchBot/issues" 14 | }, 15 | "repository": "kawaiiDango/reverseSearchBot", 16 | "license": "ISC", 17 | "dependencies": { 18 | "async-mutex": "^0.5.0", 19 | "cheerio": "^1.0.0-rc.12", 20 | "lru-cache": "^10.3.1", 21 | "fetch-socks": "^1.3.0", 22 | "telegraf": "^4.16.3" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /settings/private.js.example: -------------------------------------------------------------------------------- 1 | export const privateSettings = { 2 | botToken: "tg bot token", 3 | analKey: "amplitude.com analytics key", 4 | botName: "mybot", 5 | webhookPort:443, 6 | webhookHost: "127.0.0.1", 7 | webhookEndpoint: "https://my.site/secretPath", 8 | socksProxyHost: "0.0.0.0", 9 | socksProxyPort: 1234, 10 | changeProxyCommand: "", 11 | favouriteGroups: [ 12 | -111111 13 | ] 14 | } -------------------------------------------------------------------------------- /settings/settings.js: -------------------------------------------------------------------------------- 1 | import { privateSettings as _privateSettings } from "./private.js"; 2 | 3 | export const privateSettings = _privateSettings; 4 | 5 | export const urls = { 6 | sauceNao: "https://saucenao.com/search.php?", 7 | mal: "https://myanimelist.net/anime.php?", 8 | analUrl: "https://api2.amplitude.com/2/httpapi", 9 | pixiv_id: "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=", 10 | danbooru_id: "https://danbooru.donmai.us/post/show/", 11 | gelbooru_id: "https://gelbooru.com/index.php?page=post&s=view&id=", 12 | sankaku_id: "https://chan.sankakucomplex.com/post/show/", 13 | "anime-pictures_id": "https://anime-pictures.net/pictures/view_post/", 14 | imdb_id: "https://www.imdb.com/title/", 15 | anidb_aid: "https://anidb.net/perl-bin/animedb.pl?show=anime&aid=", 16 | e621_id: "https://e621.net/post/show/", 17 | yandere_id: "https://yande.re/post/show/", 18 | drawr_id: "https://drawr.net/show.php?id=", 19 | da_id: "https://deviantart.com/view/", 20 | pawoo_id: "https://pawoo.net/@", 21 | bcy_id: "https://bcy.net/", 22 | url: "", 23 | /* wont add: 24 | seiga_id 25 | */ 26 | }; 27 | 28 | export const id_buttonName = { 29 | pixiv_id: "Pixiv", 30 | danbooru_id: "Danbooru", 31 | gelbooru_id: "Gelbooru", 32 | sankaku_id: "Sankaku", 33 | "anime-pictures_id": "Anime-Pictures", 34 | imdb_id: "IMDb", 35 | anidb_aid: "AniDB", 36 | e621_id: "e621", 37 | yandere_id: "yandere", 38 | drawr_id: "Drawr", 39 | da_id: "deviantArt", 40 | pawoo_id: "Pawoo", 41 | bcy_id: "BCY", 42 | url: "", 43 | 44 | loading: "🍝", 45 | }; 46 | 47 | export const msgs = { 48 | loading: "Pouring some sauce on it...", 49 | zeroResult: 50 | "No sauce found; this bot works only with uncropped anime/2d art.", 51 | help: 52 | "Send me an image, a sticker, an image file or a GIF to find its source Right Nao.\n\n" + 53 | "If I'm in a group, reply to a media with /sauce@" + 54 | privateSettings.botName + 55 | " or /source@" + 56 | privateSettings.botName + 57 | "", 58 | reachLimitation: "The request limit has reached, try again after some time.", 59 | unknownError: "Oopsie doopsie, I did a fucky wucky", 60 | invalidFileId: "Invalid file", 61 | keywordHelp: 62 | "Don't just click me like that. \n\nPM me a pic, GIF, a sticker, or an image file\n\nIf I'm in a group, reply with /source@" + 63 | privateSettings.botName + 64 | " or /sauce@" + 65 | privateSettings.botName + 66 | " to find its source.", 67 | privacyPolicy: 68 | "The bot Reverse Search Bot, counts unique telegram user IDs for analytics.", 69 | }; 70 | 71 | export const floodProtect = { 72 | message: "aaaaah, slow down...", 73 | interval: 30, 74 | msgLimit: 3, 75 | }; 76 | 77 | export const sauceNaoParams = { 78 | db: 999, 79 | // output_type: 2, 80 | testmode: 1, 81 | numres: 5, 82 | minSimilarity: 60, 83 | tolerance: 7, 84 | }; 85 | 86 | export const keywords = /^(sauce|source|what\?)$/i; 87 | export const commands = [ 88 | "sauce", 89 | "source", 90 | "sauce@" + privateSettings.botName, 91 | "source@" + privateSettings.botName, 92 | ]; 93 | export const userAgent = 94 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_4_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4.1 Safari/605.1.15"; 95 | -------------------------------------------------------------------------------- /tools/tools.js: -------------------------------------------------------------------------------- 1 | export async function editMessageText(bot, msg, text, extras) { 2 | try { 3 | if (msg.inline_message_id) 4 | await bot.telegram.editMessageText( 5 | undefined, 6 | undefined, 7 | msg.inline_message_id, 8 | text, 9 | extras 10 | ); 11 | else 12 | await bot.telegram.editMessageText( 13 | msg.chat.id, 14 | msg.message_id, 15 | undefined, 16 | text, 17 | extras 18 | ); 19 | } catch (e) { 20 | console.log("editMessageText failed"); 21 | } 22 | } 23 | export function json2query(params) { 24 | const query = Object.keys(params) 25 | .map((k) => encodeURIComponent(k) + "=" + encodeURIComponent(params[k])) 26 | .join("&"); 27 | return query; 28 | } 29 | 30 | export function isSupportedExt(filename) { 31 | if (!filename) return false; 32 | const ext = filename.substr(filename.lastIndexOf(".") + 1).toLowerCase(); 33 | if ( 34 | ext == "jpg" || 35 | ext == "jpeg" || 36 | ext == "gif" || 37 | ext == "png" || 38 | ext == "webp" || 39 | ext == "bmp" 40 | ) 41 | return true; 42 | return false; 43 | } 44 | --------------------------------------------------------------------------------